@frontmcp/adapters 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.security.js","sourceRoot":"","sources":["../../../src/openapi/openapi.security.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n SecurityResolver,\n createSecurityContext,\n type McpOpenAPITool,\n type SecurityContext,\n} from 'mcp-from-openapi';\nimport type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';\nimport type { OpenApiAdapterOptions } from './openapi.types';\n\n/**\n * Security scheme information extracted from OpenAPI spec\n */\nexport interface SecuritySchemeInfo {\n name: string;\n type: string;\n scheme?: string;\n in?: string;\n description?: string;\n}\n\n/**\n * Security validation result\n */\nexport interface SecurityValidationResult {\n valid: boolean;\n missingMappings: string[];\n warnings: string[];\n securityRiskScore: 'low' | 'medium' | 'high';\n}\n\n/**\n * Resolve security context from FrontMCP auth info with support for multiple auth providers\n *\n * @param tool - OpenAPI tool to resolve security for\n * @param authInfo - FrontMCP authentication info\n * @param options - Adapter options with auth configuration\n * @returns Security context for resolver\n */\nexport async function createSecurityContextFromAuth(\n tool: McpOpenAPITool,\n authInfo: AuthInfo,\n options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth'>\n): Promise<SecurityContext> {\n // 1. Use custom security resolver if provided (highest priority)\n if (options.securityResolver) {\n return await options.securityResolver(tool, authInfo);\n }\n\n // 2. Use auth provider mapper if provided\n if (options.authProviderMapper) {\n const context = createSecurityContext({});\n\n // Find all security schemes used by this tool\n const securitySchemes = new Set<string>();\n for (const mapper of tool.mapper) {\n if (mapper.security?.scheme) {\n securitySchemes.add(mapper.security.scheme);\n }\n }\n\n // Map each security scheme to its auth provider\n for (const scheme of securitySchemes) {\n const authExtractor = options.authProviderMapper[scheme];\n if (authExtractor) {\n const token = authExtractor(authInfo);\n if (token) {\n // Store in jwt field for http bearer auth\n // For other auth types, you can extend this logic\n context.jwt = token;\n break; // Use first matching provider\n }\n }\n }\n\n // If no provider matched but we have a default token, use it\n if (!context.jwt && authInfo.token) {\n context.jwt = authInfo.token;\n }\n\n return context;\n }\n\n // 3. Use static auth if provided\n if (options.staticAuth) {\n return createSecurityContext(options.staticAuth);\n }\n\n // 4. Default: use main JWT token from auth context\n return createSecurityContext({\n jwt: authInfo?.token,\n });\n}\n\n/**\n * Extract all security schemes used by a set of tools\n *\n * @param tools - OpenAPI tools\n * @returns Set of security scheme names\n */\nexport function extractSecuritySchemes(tools: McpOpenAPITool[]): Set<string> {\n const schemes = new Set<string>();\n\n for (const tool of tools) {\n for (const mapper of tool.mapper) {\n if (mapper.security?.scheme) {\n schemes.add(mapper.security.scheme);\n }\n }\n }\n\n return schemes;\n}\n\n/**\n * Validate security configuration against OpenAPI security requirements\n *\n * @param tools - OpenAPI tools\n * @param options - Adapter options\n * @returns Validation result with errors and warnings\n */\nexport function validateSecurityConfiguration(\n tools: McpOpenAPITool[],\n options: Pick<\n OpenApiAdapterOptions,\n 'securityResolver' | 'authProviderMapper' | 'staticAuth' | 'generateOptions'\n >\n): SecurityValidationResult {\n const result: SecurityValidationResult = {\n valid: true,\n missingMappings: [],\n warnings: [],\n securityRiskScore: 'low',\n };\n\n // Extract all security schemes used\n const securitySchemes = extractSecuritySchemes(tools);\n\n // If includeSecurityInInput is true, auth is provided by user (high security risk)\n const includeSecurityInInput = options.generateOptions?.includeSecurityInInput ?? false;\n\n if (includeSecurityInInput) {\n result.securityRiskScore = 'high';\n result.warnings.push(\n 'SECURITY WARNING: includeSecurityInInput is enabled. Users will provide authentication directly in tool inputs. This increases security risk as credentials may be logged or exposed.'\n );\n // Don't validate mappings if security is in input\n return result;\n }\n\n // Check if we have custom security resolver (most flexible, low risk)\n if (options.securityResolver) {\n result.securityRiskScore = 'low';\n result.warnings.push(\n 'INFO: Using custom securityResolver. Ensure your resolver properly validates and secures credentials from context.'\n );\n return result;\n }\n\n // Check if we have static auth (medium risk - static credentials)\n if (options.staticAuth && Object.keys(options.staticAuth).length > 0) {\n result.securityRiskScore = 'medium';\n result.warnings.push(\n 'SECURITY INFO: Using staticAuth with hardcoded credentials. Ensure credentials are stored securely (environment variables, secrets manager).'\n );\n // If static auth is provided, assume it covers all schemes\n return result;\n }\n\n // Check authProviderMapper (low risk - context-based auth)\n if (options.authProviderMapper) {\n result.securityRiskScore = 'low';\n\n // Validate that all schemes have mappings\n for (const scheme of securitySchemes) {\n if (!options.authProviderMapper[scheme]) {\n result.valid = false;\n result.missingMappings.push(scheme);\n }\n }\n\n if (!result.valid) {\n result.warnings.push(\n `ERROR: Missing auth provider mappings for security schemes: ${result.missingMappings.join(', ')}`\n );\n }\n\n return result;\n }\n\n // No auth configuration provided - will use default ctx.authInfo.token\n // This only works if there's a single Bearer auth scheme\n if (securitySchemes.size > 0) {\n result.securityRiskScore = 'medium';\n result.warnings.push(\n `INFO: No auth configuration provided. Using default ctx.authInfo.token for all security schemes: ${Array.from(securitySchemes).join(', ')}`\n );\n result.warnings.push(\n 'RECOMMENDATION: For multiple auth providers, use authProviderMapper or securityResolver to map each security scheme to the correct auth provider.'\n );\n }\n\n return result;\n}\n\n/**\n * Resolve security for an OpenAPI tool with validation\n *\n * @param tool - OpenAPI tool with mapper\n * @param authInfo - FrontMCP authentication info\n * @param options - Adapter options with auth configuration\n * @returns Resolved security (headers, query params, etc.)\n * @throws Error if security cannot be resolved\n */\nexport async function resolveToolSecurity(\n tool: McpOpenAPITool,\n authInfo: AuthInfo,\n options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth'>\n) {\n const securityResolver = new SecurityResolver();\n const securityContext = await createSecurityContextFromAuth(tool, authInfo, options);\n\n // Validate that we have auth for this tool\n const hasAuth =\n securityContext.jwt ||\n securityContext.apiKey ||\n securityContext.basic ||\n securityContext.oauth2Token ||\n (securityContext.apiKeys && Object.keys(securityContext.apiKeys).length > 0) ||\n (securityContext.customHeaders && Object.keys(securityContext.customHeaders).length > 0);\n\n // Check if this tool requires security\n const requiresSecurity = tool.mapper.some((m) => m.security && m.required);\n\n if (requiresSecurity && !hasAuth) {\n // Extract security scheme names\n const schemes = tool.mapper\n .filter((m) => m.security && m.required)\n .map((m) => m.security?.scheme ?? 'unknown')\n .join(', ');\n\n throw new Error(\n `Authentication required for tool '${tool.name}' but no auth configuration found.\\n` +\n `Required security schemes: ${schemes}\\n` +\n `Solutions:\\n` +\n ` 1. Add authProviderMapper: { '${schemes.split(',')[0].trim()}': (authInfo) => authInfo.user?.token }\\n` +\n ` 2. Add securityResolver: (tool, authInfo) => ({ jwt: authInfo.token })\\n` +\n ` 3. Add staticAuth: { jwt: process.env.API_TOKEN }\\n` +\n ` 4. Set generateOptions.includeSecurityInInput: true (not recommended for production)`\n );\n }\n\n return await securityResolver.resolve(tool.mapper, securityContext);\n}\n"]}
1
+ {"version":3,"file":"openapi.security.js","sourceRoot":"","sources":["../../../src/openapi/openapi.security.ts"],"names":[],"mappings":"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","sourcesContent":["import { SecurityResolver, createSecurityContext, type McpOpenAPITool, type SecurityContext } from 'mcp-from-openapi';\nimport type { FrontMcpContext } from '@frontmcp/sdk';\nimport type { OpenApiAdapterOptions } from './openapi.types';\n\n/**\n * Security scheme information extracted from OpenAPI spec\n */\nexport interface SecuritySchemeInfo {\n name: string;\n type: string;\n scheme?: string;\n in?: string;\n description?: string;\n}\n\n/**\n * Security validation result\n */\nexport interface SecurityValidationResult {\n valid: boolean;\n missingMappings: string[];\n warnings: string[];\n securityRiskScore: 'low' | 'medium' | 'high';\n}\n\n/**\n * Resolve security context from FrontMCP context with support for multiple auth providers\n *\n * @param tool - OpenAPI tool to resolve security for\n * @param ctx - FrontMCP request context with authInfo, sessionId, traceId, etc.\n * @param options - Adapter options with auth configuration\n * @returns Security context for resolver\n */\nexport async function createSecurityContextFromAuth(\n tool: McpOpenAPITool,\n ctx: FrontMcpContext,\n options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth'>,\n): Promise<SecurityContext> {\n // 1. Use custom security resolver if provided (highest priority)\n if (options.securityResolver) {\n return await options.securityResolver(tool, ctx);\n }\n\n // 2. Use auth provider mapper if provided\n if (options.authProviderMapper) {\n const context = createSecurityContext({});\n\n // Find all security schemes used by this tool\n const securitySchemes = new Set<string>();\n for (const mapper of tool.mapper) {\n if (mapper.security?.scheme) {\n securitySchemes.add(mapper.security.scheme);\n }\n }\n\n // Map each security scheme to its auth provider\n // Process all schemes - first matching token for each auth type (jwt, apiKey, basic, oauth2Token)\n for (const scheme of securitySchemes) {\n const authExtractor = options.authProviderMapper[scheme];\n if (authExtractor) {\n try {\n const token = authExtractor(ctx);\n\n // Validate return type - must be string or undefined/null\n if (token !== undefined && token !== null && typeof token !== 'string') {\n throw new Error(\n `authProviderMapper['${scheme}'] must return a string or undefined, ` + `but returned: ${typeof token}`,\n );\n }\n\n // Reject empty string tokens explicitly - indicates misconfiguration\n if (token === '') {\n throw new Error(\n `authProviderMapper['${scheme}'] returned empty string. ` +\n `Return undefined/null if no token is available, or provide a valid token.`,\n );\n }\n\n if (token) {\n // Route token to correct context field based on scheme type\n // Look up the scheme info from the mapper to determine type\n const schemeMapper = tool.mapper.find((m) => m.security?.scheme === scheme);\n const schemeType = schemeMapper?.security?.type?.toLowerCase();\n const httpScheme = schemeMapper?.security?.httpScheme?.toLowerCase();\n\n // Route based on security scheme type (first token for each type wins)\n if (schemeType === 'apikey') {\n if (!context.apiKey) {\n context.apiKey = token;\n }\n } else if (schemeType === 'http' && httpScheme === 'basic') {\n if (!context.basic) {\n context.basic = token;\n }\n } else if (schemeType === 'oauth2') {\n if (!context.oauth2Token) {\n context.oauth2Token = token;\n }\n } else {\n // Default to jwt for http bearer and unknown types\n if (!context.jwt) {\n context.jwt = token;\n }\n }\n // Continue checking other schemes - don't break\n // This allows validation to see all configured providers\n }\n } catch (err) {\n // Re-throw validation errors as-is\n if (err instanceof Error && err.message.includes('authProviderMapper')) {\n throw err;\n }\n // Wrap other errors with context\n const errorMessage = err instanceof Error ? err.message : String(err);\n throw new Error(`authProviderMapper['${scheme}'] threw an error: ${errorMessage}`);\n }\n }\n }\n\n // If no auth was set from providers, fall back to ctx.authInfo.token\n // Only fall back if ALL auth fields are empty (not just jwt)\n const hasAnyAuth = context.jwt || context.apiKey || context.basic || context.oauth2Token;\n const authToken = ctx.authInfo?.token;\n if (!hasAnyAuth && authToken) {\n // Validate type before assignment to prevent non-string values\n if (typeof authToken !== 'string') {\n throw new Error(`authInfo.token must be a string, but got: ${typeof authToken}`);\n }\n context.jwt = authToken;\n }\n\n return context;\n }\n\n // 3. Use static auth if provided\n if (options.staticAuth) {\n return createSecurityContext(options.staticAuth);\n }\n\n // 4. Default: use main JWT token from auth context\n return createSecurityContext({\n jwt: ctx.authInfo?.token,\n });\n}\n\n/**\n * Extract all security schemes used by a set of tools\n *\n * @param tools - OpenAPI tools\n * @returns Set of security scheme names\n */\nexport function extractSecuritySchemes(tools: McpOpenAPITool[]): Set<string> {\n const schemes = new Set<string>();\n\n for (const tool of tools) {\n for (const mapper of tool.mapper) {\n if (mapper.security?.scheme) {\n schemes.add(mapper.security.scheme);\n }\n }\n }\n\n return schemes;\n}\n\n/**\n * Validate security configuration against OpenAPI security requirements\n *\n * @param tools - OpenAPI tools\n * @param options - Adapter options\n * @returns Validation result with errors and warnings\n */\nexport function validateSecurityConfiguration(\n tools: McpOpenAPITool[],\n options: Pick<\n OpenApiAdapterOptions,\n 'securityResolver' | 'authProviderMapper' | 'staticAuth' | 'generateOptions' | 'securitySchemesInInput'\n >,\n): SecurityValidationResult {\n const result: SecurityValidationResult = {\n valid: true,\n missingMappings: [],\n warnings: [],\n securityRiskScore: 'low',\n };\n\n // Extract all security schemes used\n const securitySchemes = extractSecuritySchemes(tools);\n\n // If includeSecurityInInput is true, auth is provided by user (high security risk)\n const includeSecurityInInput = options.generateOptions?.includeSecurityInInput ?? false;\n\n if (includeSecurityInInput) {\n result.securityRiskScore = 'high';\n result.warnings.push(\n 'SECURITY WARNING: includeSecurityInInput is enabled. Users will provide authentication directly in tool inputs. This increases security risk as credentials may be logged or exposed.',\n );\n // Don't validate mappings if security is in input\n return result;\n }\n\n // Check if we have custom security resolver (most flexible, low risk)\n if (options.securityResolver) {\n result.securityRiskScore = 'low';\n result.warnings.push(\n 'INFO: Using custom securityResolver. Ensure your resolver properly validates and secures credentials from context.',\n );\n return result;\n }\n\n // Check if we have static auth (medium risk - static credentials)\n if (options.staticAuth && Object.keys(options.staticAuth).length > 0) {\n result.securityRiskScore = 'medium';\n result.warnings.push(\n 'SECURITY INFO: Using staticAuth with hardcoded credentials. Ensure credentials are stored securely (environment variables, secrets manager).',\n );\n // If static auth is provided, assume it covers all schemes\n return result;\n }\n\n // Get schemes that will be provided via input (don't need mapping)\n const schemesInInput = new Set(options.securitySchemesInInput || []);\n\n // Check authProviderMapper (low risk - context-based auth)\n if (options.authProviderMapper || schemesInInput.size > 0) {\n result.securityRiskScore = schemesInInput.size > 0 ? 'medium' : 'low';\n\n // Log info about per-scheme control\n if (schemesInInput.size > 0) {\n result.warnings.push(\n `INFO: Per-scheme security control enabled. Schemes in input: ${Array.from(schemesInInput).join(', ')}`,\n );\n }\n\n // Validate that all schemes have mappings (except those in input)\n for (const scheme of securitySchemes) {\n // Skip schemes that will be provided via input\n if (schemesInInput.has(scheme)) {\n continue;\n }\n // Check if there's a mapping for this scheme\n if (!options.authProviderMapper?.[scheme]) {\n result.valid = false;\n result.missingMappings.push(scheme);\n }\n }\n\n if (!result.valid) {\n result.warnings.push(\n `ERROR: Missing auth provider mappings for security schemes: ${result.missingMappings.join(', ')}`,\n );\n }\n\n return result;\n }\n\n // No auth configuration provided - will use default ctx.authInfo.token\n // This only works if there's a single Bearer auth scheme\n if (securitySchemes.size > 0) {\n result.securityRiskScore = 'medium';\n result.warnings.push(\n `INFO: No auth configuration provided. Using default ctx.authInfo.token for all security schemes: ${Array.from(\n securitySchemes,\n ).join(', ')}`,\n );\n result.warnings.push(\n 'RECOMMENDATION: For multiple auth providers, use authProviderMapper or securityResolver to map each security scheme to the correct auth provider.',\n );\n }\n\n return result;\n}\n\n/**\n * Resolve security for an OpenAPI tool with validation\n *\n * @param tool - OpenAPI tool with mapper\n * @param ctx - FrontMCP request context with authInfo, sessionId, traceId, etc.\n * @param options - Adapter options with auth configuration\n * @returns Resolved security (headers, query params, etc.)\n * @throws Error if security cannot be resolved\n */\nexport async function resolveToolSecurity(\n tool: McpOpenAPITool,\n ctx: FrontMcpContext,\n options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth'>,\n) {\n const securityResolver = new SecurityResolver();\n const securityContext = await createSecurityContextFromAuth(tool, ctx, options);\n\n // Validate that we have auth for this tool\n const hasAuth =\n securityContext.jwt ||\n securityContext.apiKey ||\n securityContext.basic ||\n securityContext.oauth2Token ||\n (securityContext.apiKeys && Object.keys(securityContext.apiKeys).length > 0) ||\n (securityContext.customHeaders && Object.keys(securityContext.customHeaders).length > 0);\n\n // Check if this tool requires security\n // A tool requires security ONLY if a mapper has security with required=true\n // Optional security schemes (required=false or undefined) should not block requests\n const requiresSecurity = tool.mapper.some((m) => m.security && m.required === true);\n\n if (requiresSecurity && !hasAuth) {\n // Extract required security scheme names for error message\n const requiredSchemes = tool.mapper\n .filter((m) => m.security && m.required === true)\n .map((m) => m.security?.scheme ?? 'unknown');\n const uniqueSchemes = [...new Set(requiredSchemes)];\n const schemesStr = uniqueSchemes.join(', ') || 'unknown';\n const firstScheme = uniqueSchemes[0] || 'BearerAuth';\n\n throw new Error(\n `Authentication required for tool '${tool.name}' but no auth configuration found.\\n` +\n `Required security schemes: ${schemesStr}\\n` +\n `Solutions:\\n` +\n ` 1. Add authProviderMapper: { '${firstScheme}': (ctx) => ctx.authInfo.user?.token }\\n` +\n ` 2. Add securityResolver: (tool, ctx) => ({ jwt: ctx.authInfo.token })\\n` +\n ` 3. Add staticAuth: { jwt: process.env.API_TOKEN }\\n` +\n ` 4. Set generateOptions.includeSecurityInInput: true (not recommended for production)`,\n );\n }\n\n return await securityResolver.resolve(tool.mapper, securityContext);\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { FrontMcpLogger } from '@frontmcp/sdk';
1
2
  import type { McpOpenAPITool } from 'mcp-from-openapi';
2
3
  import type { OpenApiAdapterOptions } from './openapi.types';
3
4
  /**
@@ -5,6 +6,7 @@ import type { OpenApiAdapterOptions } from './openapi.types';
5
6
  *
6
7
  * @param openapiTool - OpenAPI tool with mapper
7
8
  * @param options - Adapter options
9
+ * @param logger - Logger instance
8
10
  * @returns FrontMCP tool
9
11
  */
10
- export declare function createOpenApiTool(openapiTool: McpOpenAPITool, options: OpenApiAdapterOptions): () => void;
12
+ export declare function createOpenApiTool(openapiTool: McpOpenAPITool, options: OpenApiAdapterOptions, logger: FrontMcpLogger): () => void;
@@ -6,76 +6,262 @@ const sdk_1 = require("@frontmcp/sdk");
6
6
  const zod_from_json_schema_1 = require("zod-from-json-schema");
7
7
  const openapi_utils_1 = require("./openapi.utils");
8
8
  const openapi_security_1 = require("./openapi.security");
9
+ const openapi_frontmcp_schema_1 = require("./openapi.frontmcp-schema");
9
10
  /**
10
11
  * Create a FrontMCP tool from an OpenAPI tool definition
11
12
  *
12
13
  * @param openapiTool - OpenAPI tool with mapper
13
14
  * @param options - Adapter options
15
+ * @param logger - Logger instance
14
16
  * @returns FrontMCP tool
15
17
  */
16
- function createOpenApiTool(openapiTool, options) {
18
+ function createOpenApiTool(openapiTool, options, logger) {
19
+ // Cast metadata to extended type (includes adapter-added fields)
20
+ const metadata = openapiTool.metadata;
21
+ // Get transforms stored in metadata by adapter
22
+ const inputTransforms = metadata.adapter?.inputTransforms ?? [];
23
+ const toolTransform = metadata.adapter?.toolTransform ?? {};
24
+ // Validate and parse x-frontmcp extension from OpenAPI spec
25
+ const frontmcpValidation = (0, openapi_frontmcp_schema_1.validateFrontMcpExtension)(metadata.frontmcp, openapiTool.name, logger);
26
+ const frontmcpExt = frontmcpValidation.data;
17
27
  // Convert JSON Schema to Zod schema for input validation
18
- const inputSchema = getZodSchemaFromJsonSchema(openapiTool.inputSchema, openapiTool.name);
19
- return (0, sdk_1.tool)({
28
+ const schemaResult = getZodSchemaFromJsonSchema(openapiTool.inputSchema, openapiTool.name, logger);
29
+ // Build tool metadata with transforms applied
30
+ // Priority: OpenAPI x-frontmcp → toolTransforms (adapter can override spec)
31
+ const toolMetadata = {
20
32
  id: openapiTool.name,
21
33
  name: openapiTool.name,
22
34
  description: openapiTool.description,
23
- inputSchema: inputSchema.shape || {},
35
+ inputSchema: schemaResult.schema.shape || {},
24
36
  rawInputSchema: openapiTool.inputSchema,
25
- })(async (input, ctx) => {
26
- // 1. Resolve security from context
27
- const security = await (0, openapi_security_1.resolveToolSecurity)(openapiTool, ctx.authInfo, options);
28
- // 2. Build request from mapper
29
- const { url, headers, body: requestBody } = (0, openapi_utils_1.buildRequest)(openapiTool, input, security, options.baseUrl);
30
- // 3. Apply additional headers
37
+ };
38
+ // Track schema conversion failure in metadata for debugging
39
+ if (schemaResult.conversionFailed) {
40
+ toolMetadata['_schemaConversionFailed'] = true;
41
+ toolMetadata['_schemaConversionError'] = schemaResult.error;
42
+ }
43
+ // 1. Apply validated x-frontmcp extensions from OpenAPI spec (base layer)
44
+ if (frontmcpExt) {
45
+ if (frontmcpExt.annotations) {
46
+ toolMetadata['annotations'] = { ...frontmcpExt.annotations };
47
+ }
48
+ if (frontmcpExt.tags) {
49
+ toolMetadata['tags'] = [...frontmcpExt.tags];
50
+ }
51
+ if (frontmcpExt.hideFromDiscovery !== undefined) {
52
+ toolMetadata['hideFromDiscovery'] = frontmcpExt.hideFromDiscovery;
53
+ }
54
+ if (frontmcpExt.examples) {
55
+ toolMetadata['examples'] = [...frontmcpExt.examples];
56
+ }
57
+ if (frontmcpExt.cache) {
58
+ toolMetadata['cache'] = { ...frontmcpExt.cache };
59
+ }
60
+ if (frontmcpExt.codecall) {
61
+ toolMetadata['codecall'] = { ...frontmcpExt.codecall };
62
+ }
63
+ }
64
+ // 2. Apply toolTransforms (adapter-level overrides)
65
+ if (toolTransform.annotations) {
66
+ toolMetadata['annotations'] = {
67
+ ...(toolMetadata['annotations'] || {}),
68
+ ...toolTransform.annotations,
69
+ };
70
+ }
71
+ if (toolTransform.tags) {
72
+ const existingTags = toolMetadata['tags'] || [];
73
+ toolMetadata['tags'] = [...existingTags, ...toolTransform.tags];
74
+ }
75
+ if (toolTransform.hideFromDiscovery !== undefined) {
76
+ toolMetadata['hideFromDiscovery'] = toolTransform.hideFromDiscovery;
77
+ }
78
+ if (toolTransform.examples) {
79
+ const existingExamples = toolMetadata['examples'] || [];
80
+ toolMetadata['examples'] = [...existingExamples, ...toolTransform.examples];
81
+ }
82
+ if (toolTransform.ui) {
83
+ toolMetadata['ui'] = toolTransform.ui;
84
+ }
85
+ return (0, sdk_1.tool)(toolMetadata)(async (input, toolCtx) => {
86
+ // Get the FrontMcpContext for full context access (sessionId, traceId, authInfo, etc.)
87
+ const ctx = toolCtx.context;
88
+ // 1. Inject transformed values (from inputTransforms)
89
+ const transformContext = {
90
+ ctx,
91
+ env: process.env,
92
+ tool: openapiTool,
93
+ };
94
+ const injectedInput = await injectTransformedValues(input, inputTransforms, transformContext);
95
+ // 2. Resolve security from context
96
+ const security = await (0, openapi_security_1.resolveToolSecurity)(openapiTool, ctx, options);
97
+ // 3. Build request from mapper (now uses injectedInput)
98
+ const { url, headers, body: requestBody } = (0, openapi_utils_1.buildRequest)(openapiTool, injectedInput, security, options.baseUrl);
99
+ // 4. Apply additional headers
31
100
  (0, openapi_utils_1.applyAdditionalHeaders)(headers, options.additionalHeaders);
32
- // 4. Apply custom headers mapper
101
+ // 5. Apply custom headers mapper with error handling
33
102
  if (options.headersMapper) {
34
- const mappedHeaders = options.headersMapper(ctx.authInfo, headers);
35
- mappedHeaders.forEach((value, key) => {
36
- headers.set(key, value);
37
- });
103
+ try {
104
+ const mappedHeaders = options.headersMapper(ctx, headers);
105
+ if (mappedHeaders && typeof mappedHeaders.forEach === 'function') {
106
+ mappedHeaders.forEach((value, key) => {
107
+ headers.set(key, value);
108
+ });
109
+ }
110
+ }
111
+ catch (err) {
112
+ const errorMessage = err instanceof Error ? err.message : String(err);
113
+ throw new Error(`headersMapper failed for tool '${openapiTool.name}': ${errorMessage}`);
114
+ }
38
115
  }
39
- // 5. Apply custom body mapper
116
+ // 6. Apply custom body mapper with error handling
40
117
  let finalBody = requestBody;
41
118
  if (options.bodyMapper && requestBody) {
42
- finalBody = options.bodyMapper(ctx.authInfo, requestBody);
119
+ try {
120
+ finalBody = options.bodyMapper(ctx, requestBody);
121
+ }
122
+ catch (err) {
123
+ const errorMessage = err instanceof Error ? err.message : String(err);
124
+ throw new Error(`bodyMapper failed for tool '${openapiTool.name}': ${errorMessage}`);
125
+ }
43
126
  }
44
- // 6. Set content-type if we have a body
127
+ // 7. Set content-type if we have a body
45
128
  if (finalBody && !headers.has('content-type')) {
46
129
  headers.set('content-type', 'application/json');
47
130
  }
48
- // 7. Execute request
49
- const response = await fetch(url, {
50
- method: openapiTool.metadata.method.toUpperCase(),
51
- headers,
52
- body: finalBody ? JSON.stringify(finalBody) : undefined,
53
- });
54
- // 8. Parse and return response
55
- return await (0, openapi_utils_1.parseResponse)(response);
131
+ // 8. Serialize body and check size limit
132
+ let serializedBody;
133
+ if (finalBody) {
134
+ try {
135
+ serializedBody = JSON.stringify(finalBody);
136
+ }
137
+ catch (err) {
138
+ const errorMessage = err instanceof Error ? err.message : String(err);
139
+ throw new Error(`Failed to serialize request body for tool '${openapiTool.name}': ${errorMessage}. ` +
140
+ `Body may contain circular references, BigInt, or non-serializable values.`);
141
+ }
142
+ // Check request body size limit (10MB default)
143
+ const maxRequestSize = options.maxRequestSize ?? DEFAULT_MAX_REQUEST_SIZE;
144
+ if (serializedBody.length > maxRequestSize) {
145
+ throw new Error(`Request body size (${serializedBody.length} bytes) exceeds maximum allowed (${maxRequestSize} bytes)`);
146
+ }
147
+ }
148
+ // 9. Execute request with timeout
149
+ const requestTimeout = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
150
+ const controller = new AbortController();
151
+ const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
152
+ try {
153
+ const response = await fetch(url, {
154
+ method: openapiTool.metadata.method.toUpperCase(),
155
+ headers,
156
+ body: serializedBody,
157
+ signal: controller.signal,
158
+ });
159
+ // 10. Parse and return response
160
+ return await (0, openapi_utils_1.parseResponse)(response, { maxResponseSize: options.maxResponseSize });
161
+ }
162
+ catch (err) {
163
+ if (err instanceof Error && err.name === 'AbortError') {
164
+ throw new Error(`Request timeout after ${requestTimeout}ms for tool '${openapiTool.name}'`);
165
+ }
166
+ throw err;
167
+ }
168
+ finally {
169
+ clearTimeout(timeoutId);
170
+ }
56
171
  });
57
172
  }
173
+ /** Default timeout for transform injection: 5 seconds */
174
+ const DEFAULT_TRANSFORM_TIMEOUT_MS = 5000;
175
+ /** Default request timeout: 30 seconds */
176
+ const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
177
+ /** Default max request body size: 10MB */
178
+ const DEFAULT_MAX_REQUEST_SIZE = 10 * 1024 * 1024;
179
+ /** Reserved keys that cannot be used as inputKey (prototype pollution protection) */
180
+ const RESERVED_KEYS = ['__proto__', 'constructor', 'prototype'];
181
+ /**
182
+ * Safely inject a single transform value with timeout and error handling
183
+ *
184
+ * @param transform - Transform to apply
185
+ * @param ctx - Transform context
186
+ * @param timeoutMs - Timeout in milliseconds
187
+ * @returns Injected value or undefined
188
+ */
189
+ async function safeInject(transform, ctx, timeoutMs = DEFAULT_TRANSFORM_TIMEOUT_MS) {
190
+ let timeoutId;
191
+ try {
192
+ const result = await Promise.race([
193
+ Promise.resolve(transform.inject(ctx)),
194
+ new Promise((_, reject) => {
195
+ timeoutId = setTimeout(() => reject(new Error(`Transform timeout after ${timeoutMs}ms`)), timeoutMs);
196
+ }),
197
+ ]);
198
+ return result;
199
+ }
200
+ catch (err) {
201
+ const errorMessage = err instanceof Error ? err.message : String(err);
202
+ throw new Error(`Input transform for '${transform.inputKey}' failed: ${errorMessage}`);
203
+ }
204
+ finally {
205
+ // Always clear the timeout to prevent memory leaks
206
+ if (timeoutId !== undefined) {
207
+ clearTimeout(timeoutId);
208
+ }
209
+ }
210
+ }
211
+ /**
212
+ * Validate that input is a plain object (not null, array, or primitive)
213
+ */
214
+ function isPlainObject(value) {
215
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
216
+ }
217
+ /**
218
+ * Inject transformed values into the input object
219
+ */
220
+ async function injectTransformedValues(input, transforms, ctx) {
221
+ // Validate input is a plain object to prevent prototype pollution
222
+ if (!isPlainObject(input)) {
223
+ throw new Error(`Invalid input type: expected object, got ${input === null ? 'null' : typeof input}`);
224
+ }
225
+ if (transforms.length === 0)
226
+ return input;
227
+ const result = { ...input };
228
+ for (const transform of transforms) {
229
+ // Prototype pollution protection: reject reserved keys
230
+ if (RESERVED_KEYS.includes(transform.inputKey)) {
231
+ throw new Error(`Invalid inputKey '${transform.inputKey}': reserved keys (${RESERVED_KEYS.join(', ')}) cannot be used`);
232
+ }
233
+ const value = await safeInject(transform, ctx);
234
+ if (value !== undefined) {
235
+ result[transform.inputKey] = value;
236
+ }
237
+ }
238
+ return result;
239
+ }
58
240
  /**
59
241
  * Converts a JSON Schema to a Zod schema for runtime validation
60
242
  *
61
243
  * @param jsonSchema - JSON Schema
62
244
  * @param toolName - Tool name for error reporting
63
- * @returns Zod schema
245
+ * @param logger - Logger instance
246
+ * @returns Schema conversion result with success indicator
64
247
  */
65
- function getZodSchemaFromJsonSchema(jsonSchema, toolName) {
248
+ function getZodSchemaFromJsonSchema(jsonSchema, toolName, logger) {
66
249
  if (typeof jsonSchema !== 'object' || jsonSchema === null) {
67
- return zod_1.z.object({}).passthrough();
250
+ logger.warn(`[${toolName}] No valid JSON schema provided, using permissive schema`);
251
+ return { schema: zod_1.z.looseObject({}), conversionFailed: true, error: 'No valid JSON schema' };
68
252
  }
69
253
  try {
70
254
  const zodSchema = (0, zod_from_json_schema_1.convertJsonSchemaToZod)(jsonSchema);
71
255
  if (typeof zodSchema?.parse !== 'function') {
72
256
  throw new Error('Conversion did not produce a valid Zod schema.');
73
257
  }
74
- return zodSchema;
258
+ return { schema: zodSchema, conversionFailed: false };
75
259
  }
76
260
  catch (err) {
77
- console.error(`Failed to generate Zod schema for '${toolName}':`, err);
78
- return zod_1.z.object({}).passthrough();
261
+ const errorMessage = err instanceof Error ? err.message : String(err);
262
+ logger.warn(`[${toolName}] Failed to generate Zod schema, using permissive schema. ` +
263
+ `Tool will accept any input but may fail at API level. Error: ${errorMessage}`);
264
+ return { schema: zod_1.z.looseObject({}), conversionFailed: true, error: errorMessage };
79
265
  }
80
266
  }
81
267
  //# sourceMappingURL=openapi.tool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.tool.js","sourceRoot":"","sources":["../../../src/openapi/openapi.tool.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport { tool } from '@frontmcp/sdk';\nimport { convertJsonSchemaToZod } from 'zod-from-json-schema';\nimport type { McpOpenAPITool } from 'mcp-from-openapi';\nimport type { OpenApiAdapterOptions } from './openapi.types';\nimport type { JSONSchema } from 'zod/v4/core';\n\n/** JSON Schema type from Zod v4 */\ntype JsonSchema = JSONSchema.JSONSchema;\nimport { buildRequest, applyAdditionalHeaders, parseResponse } from './openapi.utils';\nimport { resolveToolSecurity } from './openapi.security';\n\n/**\n * Create a FrontMCP tool from an OpenAPI tool definition\n *\n * @param openapiTool - OpenAPI tool with mapper\n * @param options - Adapter options\n * @returns FrontMCP tool\n */\nexport function createOpenApiTool(openapiTool: McpOpenAPITool, options: OpenApiAdapterOptions) {\n // Convert JSON Schema to Zod schema for input validation\n const inputSchema = getZodSchemaFromJsonSchema(openapiTool.inputSchema, openapiTool.name);\n\n return tool({\n id: openapiTool.name,\n name: openapiTool.name,\n description: openapiTool.description,\n inputSchema: inputSchema.shape || {},\n rawInputSchema: openapiTool.inputSchema,\n })(async (input, ctx) => {\n // 1. Resolve security from context\n const security = await resolveToolSecurity(openapiTool, ctx.authInfo, options);\n\n // 2. Build request from mapper\n const { url, headers, body: requestBody } = buildRequest(openapiTool, input, security, options.baseUrl);\n\n // 3. Apply additional headers\n applyAdditionalHeaders(headers, options.additionalHeaders);\n\n // 4. Apply custom headers mapper\n if (options.headersMapper) {\n const mappedHeaders = options.headersMapper(ctx.authInfo, headers);\n mappedHeaders.forEach((value, key) => {\n headers.set(key, value);\n });\n }\n\n // 5. Apply custom body mapper\n let finalBody = requestBody;\n if (options.bodyMapper && requestBody) {\n finalBody = options.bodyMapper(ctx.authInfo, requestBody);\n }\n\n // 6. Set content-type if we have a body\n if (finalBody && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n\n // 7. Execute request\n const response = await fetch(url, {\n method: openapiTool.metadata.method.toUpperCase(),\n headers,\n body: finalBody ? JSON.stringify(finalBody) : undefined,\n });\n\n // 8. Parse and return response\n return await parseResponse(response);\n });\n}\n\n/**\n * Converts a JSON Schema to a Zod schema for runtime validation\n *\n * @param jsonSchema - JSON Schema\n * @param toolName - Tool name for error reporting\n * @returns Zod schema\n */\nfunction getZodSchemaFromJsonSchema(jsonSchema: JsonSchema, toolName: string): z.ZodObject {\n if (typeof jsonSchema !== 'object' || jsonSchema === null) {\n return z.object({}).passthrough();\n }\n\n try {\n const zodSchema = convertJsonSchemaToZod(jsonSchema);\n if (typeof zodSchema?.parse !== 'function') {\n throw new Error('Conversion did not produce a valid Zod schema.');\n }\n return zodSchema as unknown as z.ZodObject;\n } catch (err: unknown) {\n console.error(`Failed to generate Zod schema for '${toolName}':`, err);\n return z.object({}).passthrough();\n }\n}\n"]}
1
+ {"version":3,"file":"openapi.tool.js","sourceRoot":"","sources":["../../../src/openapi/openapi.tool.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport { tool, FrontMcpLogger } from '@frontmcp/sdk';\nimport { convertJsonSchemaToZod } from 'zod-from-json-schema';\nimport type { McpOpenAPITool } from 'mcp-from-openapi';\nimport type {\n OpenApiAdapterOptions,\n InputTransformContext,\n ExtendedToolMetadata,\n InputTransform,\n} from './openapi.types';\nimport type { JSONSchema } from 'zod/v4/core';\n\n/** JSON Schema type from Zod v4 */\ntype JsonSchema = JSONSchema.JSONSchema;\nimport { buildRequest, applyAdditionalHeaders, parseResponse } from './openapi.utils';\nimport { resolveToolSecurity } from './openapi.security';\nimport { validateFrontMcpExtension, type ValidatedFrontMcpExtension } from './openapi.frontmcp-schema';\n\n/**\n * Create a FrontMCP tool from an OpenAPI tool definition\n *\n * @param openapiTool - OpenAPI tool with mapper\n * @param options - Adapter options\n * @param logger - Logger instance\n * @returns FrontMCP tool\n */\nexport function createOpenApiTool(openapiTool: McpOpenAPITool, options: OpenApiAdapterOptions, logger: FrontMcpLogger) {\n // Cast metadata to extended type (includes adapter-added fields)\n const metadata = openapiTool.metadata as ExtendedToolMetadata;\n\n // Get transforms stored in metadata by adapter\n const inputTransforms = metadata.adapter?.inputTransforms ?? [];\n const toolTransform = metadata.adapter?.toolTransform ?? {};\n\n // Validate and parse x-frontmcp extension from OpenAPI spec\n const frontmcpValidation = validateFrontMcpExtension(metadata.frontmcp, openapiTool.name, logger);\n const frontmcpExt: ValidatedFrontMcpExtension | null = frontmcpValidation.data;\n\n // Convert JSON Schema to Zod schema for input validation\n const schemaResult = getZodSchemaFromJsonSchema(openapiTool.inputSchema, openapiTool.name, logger);\n\n // Build tool metadata with transforms applied\n // Priority: OpenAPI x-frontmcp → toolTransforms (adapter can override spec)\n const toolMetadata: Record<string, unknown> = {\n id: openapiTool.name,\n name: openapiTool.name,\n description: openapiTool.description,\n inputSchema: schemaResult.schema.shape || {},\n rawInputSchema: openapiTool.inputSchema,\n };\n\n // Track schema conversion failure in metadata for debugging\n if (schemaResult.conversionFailed) {\n toolMetadata['_schemaConversionFailed'] = true;\n toolMetadata['_schemaConversionError'] = schemaResult.error;\n }\n\n // 1. Apply validated x-frontmcp extensions from OpenAPI spec (base layer)\n if (frontmcpExt) {\n if (frontmcpExt.annotations) {\n toolMetadata['annotations'] = { ...frontmcpExt.annotations };\n }\n if (frontmcpExt.tags) {\n toolMetadata['tags'] = [...frontmcpExt.tags];\n }\n if (frontmcpExt.hideFromDiscovery !== undefined) {\n toolMetadata['hideFromDiscovery'] = frontmcpExt.hideFromDiscovery;\n }\n if (frontmcpExt.examples) {\n toolMetadata['examples'] = [...frontmcpExt.examples];\n }\n if (frontmcpExt.cache) {\n toolMetadata['cache'] = { ...frontmcpExt.cache };\n }\n if (frontmcpExt.codecall) {\n toolMetadata['codecall'] = { ...frontmcpExt.codecall };\n }\n }\n\n // 2. Apply toolTransforms (adapter-level overrides)\n if (toolTransform.annotations) {\n toolMetadata['annotations'] = {\n ...((toolMetadata['annotations'] as object) || {}),\n ...toolTransform.annotations,\n };\n }\n if (toolTransform.tags) {\n const existingTags = (toolMetadata['tags'] as string[]) || [];\n toolMetadata['tags'] = [...existingTags, ...toolTransform.tags];\n }\n if (toolTransform.hideFromDiscovery !== undefined) {\n toolMetadata['hideFromDiscovery'] = toolTransform.hideFromDiscovery;\n }\n if (toolTransform.examples) {\n const existingExamples = (toolMetadata['examples'] as unknown[]) || [];\n toolMetadata['examples'] = [...existingExamples, ...toolTransform.examples];\n }\n if (toolTransform.ui) {\n toolMetadata['ui'] = toolTransform.ui;\n }\n\n return tool(toolMetadata as unknown as Parameters<typeof tool>[0])(async (input, toolCtx) => {\n // Get the FrontMcpContext for full context access (sessionId, traceId, authInfo, etc.)\n const ctx = toolCtx.context;\n\n // 1. Inject transformed values (from inputTransforms)\n const transformContext: InputTransformContext = {\n ctx,\n env: process.env,\n tool: openapiTool,\n };\n const injectedInput = await injectTransformedValues(\n input as Record<string, unknown>,\n inputTransforms,\n transformContext,\n );\n\n // 2. Resolve security from context\n const security = await resolveToolSecurity(openapiTool, ctx, options);\n\n // 3. Build request from mapper (now uses injectedInput)\n const { url, headers, body: requestBody } = buildRequest(openapiTool, injectedInput, security, options.baseUrl);\n\n // 4. Apply additional headers\n applyAdditionalHeaders(headers, options.additionalHeaders);\n\n // 5. Apply custom headers mapper with error handling\n if (options.headersMapper) {\n try {\n const mappedHeaders = options.headersMapper(ctx, headers);\n if (mappedHeaders && typeof mappedHeaders.forEach === 'function') {\n mappedHeaders.forEach((value, key) => {\n headers.set(key, value);\n });\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n throw new Error(`headersMapper failed for tool '${openapiTool.name}': ${errorMessage}`);\n }\n }\n\n // 6. Apply custom body mapper with error handling\n let finalBody = requestBody;\n if (options.bodyMapper && requestBody) {\n try {\n finalBody = options.bodyMapper(ctx, requestBody);\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n throw new Error(`bodyMapper failed for tool '${openapiTool.name}': ${errorMessage}`);\n }\n }\n\n // 7. Set content-type if we have a body\n if (finalBody && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n\n // 8. Serialize body and check size limit\n let serializedBody: string | undefined;\n if (finalBody) {\n try {\n serializedBody = JSON.stringify(finalBody);\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Failed to serialize request body for tool '${openapiTool.name}': ${errorMessage}. ` +\n `Body may contain circular references, BigInt, or non-serializable values.`,\n );\n }\n // Check request body size limit (10MB default)\n const maxRequestSize = options.maxRequestSize ?? DEFAULT_MAX_REQUEST_SIZE;\n if (serializedBody.length > maxRequestSize) {\n throw new Error(\n `Request body size (${serializedBody.length} bytes) exceeds maximum allowed (${maxRequestSize} bytes)`,\n );\n }\n }\n\n // 9. Execute request with timeout\n const requestTimeout = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), requestTimeout);\n\n try {\n const response = await fetch(url, {\n method: openapiTool.metadata.method.toUpperCase(),\n headers,\n body: serializedBody,\n signal: controller.signal,\n });\n\n // 10. Parse and return response\n return await parseResponse(response, { maxResponseSize: options.maxResponseSize });\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new Error(`Request timeout after ${requestTimeout}ms for tool '${openapiTool.name}'`);\n }\n throw err;\n } finally {\n clearTimeout(timeoutId);\n }\n });\n}\n\n/** Default timeout for transform injection: 5 seconds */\nconst DEFAULT_TRANSFORM_TIMEOUT_MS = 5000;\n\n/** Default request timeout: 30 seconds */\nconst DEFAULT_REQUEST_TIMEOUT_MS = 30000;\n\n/** Default max request body size: 10MB */\nconst DEFAULT_MAX_REQUEST_SIZE = 10 * 1024 * 1024;\n\n/** Reserved keys that cannot be used as inputKey (prototype pollution protection) */\nconst RESERVED_KEYS = ['__proto__', 'constructor', 'prototype'];\n\n/**\n * Safely inject a single transform value with timeout and error handling\n *\n * @param transform - Transform to apply\n * @param ctx - Transform context\n * @param timeoutMs - Timeout in milliseconds\n * @returns Injected value or undefined\n */\nasync function safeInject(\n transform: InputTransform,\n ctx: InputTransformContext,\n timeoutMs: number = DEFAULT_TRANSFORM_TIMEOUT_MS,\n): Promise<unknown> {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n try {\n const result = await Promise.race([\n Promise.resolve(transform.inject(ctx)),\n new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(`Transform timeout after ${timeoutMs}ms`)), timeoutMs);\n }),\n ]);\n return result;\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n throw new Error(`Input transform for '${transform.inputKey}' failed: ${errorMessage}`);\n } finally {\n // Always clear the timeout to prevent memory leaks\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n}\n\n/**\n * Validate that input is a plain object (not null, array, or primitive)\n */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Inject transformed values into the input object\n */\nasync function injectTransformedValues(\n input: unknown,\n transforms: InputTransform[],\n ctx: InputTransformContext,\n): Promise<Record<string, unknown>> {\n // Validate input is a plain object to prevent prototype pollution\n if (!isPlainObject(input)) {\n throw new Error(`Invalid input type: expected object, got ${input === null ? 'null' : typeof input}`);\n }\n\n if (transforms.length === 0) return input;\n\n const result = { ...input };\n\n for (const transform of transforms) {\n // Prototype pollution protection: reject reserved keys\n if (RESERVED_KEYS.includes(transform.inputKey)) {\n throw new Error(\n `Invalid inputKey '${transform.inputKey}': reserved keys (${RESERVED_KEYS.join(', ')}) cannot be used`,\n );\n }\n\n const value = await safeInject(transform, ctx);\n if (value !== undefined) {\n result[transform.inputKey] = value;\n }\n }\n\n return result;\n}\n\n/**\n * Result of schema conversion with success indicator\n */\ninterface SchemaConversionResult {\n schema: z.ZodObject;\n conversionFailed: boolean;\n error?: string;\n}\n\n/**\n * Converts a JSON Schema to a Zod schema for runtime validation\n *\n * @param jsonSchema - JSON Schema\n * @param toolName - Tool name for error reporting\n * @param logger - Logger instance\n * @returns Schema conversion result with success indicator\n */\nfunction getZodSchemaFromJsonSchema(\n jsonSchema: JsonSchema,\n toolName: string,\n logger: FrontMcpLogger,\n): SchemaConversionResult {\n if (typeof jsonSchema !== 'object' || jsonSchema === null) {\n logger.warn(`[${toolName}] No valid JSON schema provided, using permissive schema`);\n return { schema: z.looseObject({}), conversionFailed: true, error: 'No valid JSON schema' };\n }\n\n try {\n const zodSchema = convertJsonSchemaToZod(jsonSchema);\n if (typeof zodSchema?.parse !== 'function') {\n throw new Error('Conversion did not produce a valid Zod schema.');\n }\n return { schema: zodSchema as unknown as z.ZodObject, conversionFailed: false };\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n logger.warn(\n `[${toolName}] Failed to generate Zod schema, using permissive schema. ` +\n `Tool will accept any input but may fail at API level. Error: ${errorMessage}`,\n );\n return { schema: z.looseObject({}), conversionFailed: true, error: errorMessage };\n }\n}\n"]}