@frontmcp/adapters 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,358 +0,0 @@
1
- "use strict";
2
- /**
3
- * Zod schemas for x-frontmcp OpenAPI extension validation.
4
- *
5
- * The x-frontmcp extension allows embedding FrontMCP-specific configuration
6
- * directly in OpenAPI specs. This module provides versioned schema validation
7
- * to ensure only valid data is used and invalid fields are warned about.
8
- */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.FrontMcpExampleSchema = exports.FrontMcpCodeCallSchema = exports.FrontMcpCacheSchema = exports.FrontMcpAnnotationsSchema = exports.SUPPORTED_VERSIONS = exports.FRONTMCP_SCHEMA_VERSION = void 0;
11
- exports.validateFrontMcpExtension = validateFrontMcpExtension;
12
- const zod_1 = require("zod");
13
- /**
14
- * Current schema version for x-frontmcp extension.
15
- * Increment when making breaking changes to the schema.
16
- */
17
- exports.FRONTMCP_SCHEMA_VERSION = '1.0';
18
- /**
19
- * Supported schema versions.
20
- */
21
- exports.SUPPORTED_VERSIONS = ['1.0'];
22
- // ============================================================================
23
- // Annotations Schema (v1.0)
24
- // ============================================================================
25
- /**
26
- * Tool annotations schema - hints about tool behavior for AI clients.
27
- */
28
- exports.FrontMcpAnnotationsSchema = zod_1.z.object({
29
- /** Human-readable title for the tool */
30
- title: zod_1.z.string().optional(),
31
- /** If true, the tool does not modify its environment */
32
- readOnlyHint: zod_1.z.boolean().optional(),
33
- /** If true, the tool may perform destructive updates */
34
- destructiveHint: zod_1.z.boolean().optional(),
35
- /** If true, calling repeatedly with same args has no additional effect */
36
- idempotentHint: zod_1.z.boolean().optional(),
37
- /** If true, tool may interact with external entities */
38
- openWorldHint: zod_1.z.boolean().optional(),
39
- });
40
- // ============================================================================
41
- // Cache Schema (v1.0)
42
- // ============================================================================
43
- /**
44
- * Cache configuration schema.
45
- */
46
- exports.FrontMcpCacheSchema = zod_1.z.object({
47
- /** Time-to-live in seconds for cached responses */
48
- ttl: zod_1.z.number().int().positive().optional(),
49
- /** If true, cache window slides on each access */
50
- slideWindow: zod_1.z.boolean().optional(),
51
- });
52
- // ============================================================================
53
- // CodeCall Schema (v1.0)
54
- // ============================================================================
55
- /**
56
- * CodeCall plugin configuration schema.
57
- */
58
- exports.FrontMcpCodeCallSchema = zod_1.z.object({
59
- /** Whether this tool can be used via CodeCall */
60
- enabledInCodeCall: zod_1.z.boolean().optional(),
61
- /** If true, stays visible in list_tools when CodeCall is active */
62
- visibleInListTools: zod_1.z.boolean().optional(),
63
- });
64
- // ============================================================================
65
- // Example Schema (v1.0)
66
- // ============================================================================
67
- /**
68
- * Tool usage example schema.
69
- */
70
- exports.FrontMcpExampleSchema = zod_1.z.object({
71
- /** Description of the example */
72
- description: zod_1.z.string(),
73
- /** Example input values */
74
- input: zod_1.z.record(zod_1.z.string(), zod_1.z.any()),
75
- /** Expected output (optional) */
76
- output: zod_1.z.any().optional(),
77
- });
78
- // ============================================================================
79
- // Known Fields for Validation
80
- // ============================================================================
81
- /** Known field names for validation */
82
- const KNOWN_EXTENSION_FIELDS = new Set([
83
- 'version',
84
- 'annotations',
85
- 'cache',
86
- 'codecall',
87
- 'tags',
88
- 'hideFromDiscovery',
89
- 'examples',
90
- ]);
91
- const KNOWN_ANNOTATION_FIELDS = new Set([
92
- 'title',
93
- 'readOnlyHint',
94
- 'destructiveHint',
95
- 'idempotentHint',
96
- 'openWorldHint',
97
- ]);
98
- const KNOWN_CACHE_FIELDS = new Set(['ttl', 'slideWindow']);
99
- const KNOWN_CODECALL_FIELDS = new Set(['enabledInCodeCall', 'visibleInListTools']);
100
- // ============================================================================
101
- // Schema Validation Functions
102
- // ============================================================================
103
- /**
104
- * Validate and parse x-frontmcp extension data.
105
- *
106
- * This function:
107
- * 1. Detects the schema version (defaults to current)
108
- * 2. Validates each field against the appropriate schema
109
- * 3. Returns only valid fields, ignoring invalid ones
110
- * 4. Collects warnings for invalid/unknown fields
111
- *
112
- * @param rawData - Raw x-frontmcp data from OpenAPI spec
113
- * @param toolName - Tool name for error context
114
- * @param logger - Logger for warnings
115
- * @returns Validation result with valid data and warnings
116
- */
117
- function validateFrontMcpExtension(rawData, toolName, logger) {
118
- const warnings = [];
119
- // Handle null/undefined
120
- if (rawData === null || rawData === undefined) {
121
- return { success: true, data: null, warnings: [] };
122
- }
123
- // Must be an object
124
- if (typeof rawData !== 'object' || Array.isArray(rawData)) {
125
- warnings.push(`x-frontmcp must be an object, got ${Array.isArray(rawData) ? 'array' : typeof rawData}`);
126
- logger.warn(`[${toolName}] Invalid x-frontmcp extension: ${warnings[0]}`);
127
- return { success: false, data: null, warnings };
128
- }
129
- const data = rawData;
130
- // Detect version
131
- const version = typeof data['version'] === 'string' ? data['version'] : exports.FRONTMCP_SCHEMA_VERSION;
132
- // Check if version is supported
133
- if (!exports.SUPPORTED_VERSIONS.includes(version)) {
134
- warnings.push(`Unsupported x-frontmcp version '${version}'. Supported versions: ${exports.SUPPORTED_VERSIONS.join(', ')}`);
135
- logger.warn(`[${toolName}] ${warnings[0]}`);
136
- return { success: false, data: null, warnings };
137
- }
138
- // Extract valid fields and collect warnings
139
- const validData = extractValidFields(data, version, warnings);
140
- if (warnings.length > 0) {
141
- logger.warn(`[${toolName}] x-frontmcp extension has invalid fields that were ignored:`);
142
- warnings.forEach((w) => logger.warn(` - ${w}`));
143
- }
144
- return {
145
- success: true,
146
- data: validData,
147
- warnings,
148
- };
149
- }
150
- /**
151
- * Extract valid fields from x-frontmcp data, collecting warnings for invalid fields.
152
- */
153
- function extractValidFields(data, version, warnings) {
154
- const result = {
155
- version: version,
156
- };
157
- // Warn about unknown top-level fields
158
- for (const key of Object.keys(data)) {
159
- if (!KNOWN_EXTENSION_FIELDS.has(key)) {
160
- warnings.push(`Unknown field '${key}' in x-frontmcp (will be ignored)`);
161
- }
162
- }
163
- // Validate annotations
164
- if (data['annotations'] !== undefined) {
165
- const annotationsResult = exports.FrontMcpAnnotationsSchema.safeParse(data['annotations']);
166
- if (annotationsResult.success) {
167
- result.annotations = annotationsResult.data;
168
- }
169
- else {
170
- const issues = formatZodIssues(annotationsResult.error.issues, 'annotations');
171
- warnings.push(...issues);
172
- // Try to extract valid annotation fields
173
- result.annotations = extractValidAnnotations(data['annotations'], warnings);
174
- }
175
- }
176
- // Validate cache
177
- if (data['cache'] !== undefined) {
178
- const cacheResult = exports.FrontMcpCacheSchema.safeParse(data['cache']);
179
- if (cacheResult.success) {
180
- result.cache = cacheResult.data;
181
- }
182
- else {
183
- const issues = formatZodIssues(cacheResult.error.issues, 'cache');
184
- warnings.push(...issues);
185
- // Try to extract valid cache fields
186
- result.cache = extractValidCache(data['cache'], warnings);
187
- }
188
- }
189
- // Validate codecall
190
- if (data['codecall'] !== undefined) {
191
- const codecallResult = exports.FrontMcpCodeCallSchema.safeParse(data['codecall']);
192
- if (codecallResult.success) {
193
- result.codecall = codecallResult.data;
194
- }
195
- else {
196
- const issues = formatZodIssues(codecallResult.error.issues, 'codecall');
197
- warnings.push(...issues);
198
- // Try to extract valid codecall fields
199
- result.codecall = extractValidCodeCall(data['codecall'], warnings);
200
- }
201
- }
202
- // Validate tags
203
- if (data['tags'] !== undefined) {
204
- const tagsSchema = zod_1.z.array(zod_1.z.string());
205
- const tagsResult = tagsSchema.safeParse(data['tags']);
206
- if (tagsResult.success) {
207
- result.tags = tagsResult.data;
208
- }
209
- else {
210
- warnings.push(`Invalid 'tags': expected array of strings`);
211
- // Try to extract valid tags
212
- result.tags = extractValidTags(data['tags']);
213
- }
214
- }
215
- // Validate hideFromDiscovery
216
- if (data['hideFromDiscovery'] !== undefined) {
217
- if (typeof data['hideFromDiscovery'] === 'boolean') {
218
- result.hideFromDiscovery = data['hideFromDiscovery'];
219
- }
220
- else {
221
- warnings.push(`Invalid 'hideFromDiscovery': expected boolean, got ${typeof data['hideFromDiscovery']}`);
222
- }
223
- }
224
- // Validate examples
225
- if (data['examples'] !== undefined) {
226
- const examplesSchema = zod_1.z.array(exports.FrontMcpExampleSchema);
227
- const examplesResult = examplesSchema.safeParse(data['examples']);
228
- if (examplesResult.success) {
229
- result.examples = examplesResult.data;
230
- }
231
- else {
232
- warnings.push(`Invalid 'examples': some examples have invalid format`);
233
- // Try to extract valid examples
234
- result.examples = extractValidExamples(data['examples'], warnings);
235
- }
236
- }
237
- return result;
238
- }
239
- /**
240
- * Extract valid annotation fields.
241
- */
242
- function extractValidAnnotations(data, warnings) {
243
- if (typeof data !== 'object' || data === null)
244
- return undefined;
245
- const obj = data;
246
- const result = {};
247
- for (const field of KNOWN_ANNOTATION_FIELDS) {
248
- if (obj[field] !== undefined) {
249
- if (field === 'title' && typeof obj[field] === 'string') {
250
- result.title = obj[field];
251
- }
252
- else if (field !== 'title' && typeof obj[field] === 'boolean') {
253
- result[field] = obj[field];
254
- }
255
- }
256
- }
257
- // Warn about unknown annotation fields
258
- for (const key of Object.keys(obj)) {
259
- if (!KNOWN_ANNOTATION_FIELDS.has(key)) {
260
- warnings.push(`Unknown field 'annotations.${key}' (will be ignored)`);
261
- }
262
- }
263
- return Object.keys(result).length > 0 ? result : undefined;
264
- }
265
- /**
266
- * Extract valid cache fields.
267
- */
268
- function extractValidCache(data, warnings) {
269
- if (typeof data !== 'object' || data === null)
270
- return undefined;
271
- const obj = data;
272
- const result = {};
273
- if (typeof obj['ttl'] === 'number' && Number.isInteger(obj['ttl']) && obj['ttl'] > 0) {
274
- result.ttl = obj['ttl'];
275
- }
276
- else if (obj['ttl'] !== undefined) {
277
- warnings.push(`Invalid 'cache.ttl': expected positive integer`);
278
- }
279
- if (typeof obj['slideWindow'] === 'boolean') {
280
- result.slideWindow = obj['slideWindow'];
281
- }
282
- else if (obj['slideWindow'] !== undefined) {
283
- warnings.push(`Invalid 'cache.slideWindow': expected boolean`);
284
- }
285
- // Warn about unknown cache fields
286
- for (const key of Object.keys(obj)) {
287
- if (!KNOWN_CACHE_FIELDS.has(key)) {
288
- warnings.push(`Unknown field 'cache.${key}' (will be ignored)`);
289
- }
290
- }
291
- return Object.keys(result).length > 0 ? result : undefined;
292
- }
293
- /**
294
- * Extract valid codecall fields.
295
- */
296
- function extractValidCodeCall(data, warnings) {
297
- if (typeof data !== 'object' || data === null)
298
- return undefined;
299
- const obj = data;
300
- const result = {};
301
- if (typeof obj['enabledInCodeCall'] === 'boolean') {
302
- result.enabledInCodeCall = obj['enabledInCodeCall'];
303
- }
304
- else if (obj['enabledInCodeCall'] !== undefined) {
305
- warnings.push(`Invalid 'codecall.enabledInCodeCall': expected boolean`);
306
- }
307
- if (typeof obj['visibleInListTools'] === 'boolean') {
308
- result.visibleInListTools = obj['visibleInListTools'];
309
- }
310
- else if (obj['visibleInListTools'] !== undefined) {
311
- warnings.push(`Invalid 'codecall.visibleInListTools': expected boolean`);
312
- }
313
- // Warn about unknown codecall fields
314
- for (const key of Object.keys(obj)) {
315
- if (!KNOWN_CODECALL_FIELDS.has(key)) {
316
- warnings.push(`Unknown field 'codecall.${key}' (will be ignored)`);
317
- }
318
- }
319
- return Object.keys(result).length > 0 ? result : undefined;
320
- }
321
- /**
322
- * Extract valid tags from array.
323
- */
324
- function extractValidTags(data) {
325
- if (!Array.isArray(data))
326
- return undefined;
327
- const validTags = data.filter((item) => typeof item === 'string');
328
- return validTags.length > 0 ? validTags : undefined;
329
- }
330
- /**
331
- * Extract valid examples from array.
332
- */
333
- function extractValidExamples(data, warnings) {
334
- if (!Array.isArray(data))
335
- return undefined;
336
- const validExamples = [];
337
- for (let i = 0; i < data.length; i++) {
338
- const item = data[i];
339
- const result = exports.FrontMcpExampleSchema.safeParse(item);
340
- if (result.success) {
341
- validExamples.push(result.data);
342
- }
343
- else {
344
- warnings.push(`Invalid example at index ${i}: ${formatZodIssues(result.error.issues, `examples[${i}]`).join(', ')}`);
345
- }
346
- }
347
- return validExamples.length > 0 ? validExamples : undefined;
348
- }
349
- /**
350
- * Format Zod validation issues into readable messages.
351
- */
352
- function formatZodIssues(issues, prefix) {
353
- return issues.map((issue) => {
354
- const path = issue.path.length > 0 ? `${prefix}.${issue.path.join('.')}` : prefix;
355
- return `Invalid '${path}': ${issue.message}`;
356
- });
357
- }
358
- //# sourceMappingURL=openapi.frontmcp-schema.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"openapi.frontmcp-schema.js","sourceRoot":"","sources":["../../../src/openapi/openapi.frontmcp-schema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Zod schemas for x-frontmcp OpenAPI extension validation.\n *\n * The x-frontmcp extension allows embedding FrontMCP-specific configuration\n * directly in OpenAPI specs. This module provides versioned schema validation\n * to ensure only valid data is used and invalid fields are warned about.\n */\n\nimport { z } from 'zod';\nimport type { FrontMcpLogger } from '@frontmcp/sdk';\n\n/**\n * Current schema version for x-frontmcp extension.\n * Increment when making breaking changes to the schema.\n */\nexport const FRONTMCP_SCHEMA_VERSION = '1.0' as const;\n\n/**\n * Supported schema versions.\n */\nexport const SUPPORTED_VERSIONS = ['1.0'] as const;\nexport type FrontMcpSchemaVersion = (typeof SUPPORTED_VERSIONS)[number];\n\n// ============================================================================\n// Annotations Schema (v1.0)\n// ============================================================================\n\n/**\n * Tool annotations schema - hints about tool behavior for AI clients.\n */\nexport const FrontMcpAnnotationsSchema = z.object({\n /** Human-readable title for the tool */\n title: z.string().optional(),\n /** If true, the tool does not modify its environment */\n readOnlyHint: z.boolean().optional(),\n /** If true, the tool may perform destructive updates */\n destructiveHint: z.boolean().optional(),\n /** If true, calling repeatedly with same args has no additional effect */\n idempotentHint: z.boolean().optional(),\n /** If true, tool may interact with external entities */\n openWorldHint: z.boolean().optional(),\n});\n\nexport type FrontMcpAnnotations = z.infer<typeof FrontMcpAnnotationsSchema>;\n\n// ============================================================================\n// Cache Schema (v1.0)\n// ============================================================================\n\n/**\n * Cache configuration schema.\n */\nexport const FrontMcpCacheSchema = z.object({\n /** Time-to-live in seconds for cached responses */\n ttl: z.number().int().positive().optional(),\n /** If true, cache window slides on each access */\n slideWindow: z.boolean().optional(),\n});\n\nexport type FrontMcpCache = z.infer<typeof FrontMcpCacheSchema>;\n\n// ============================================================================\n// CodeCall Schema (v1.0)\n// ============================================================================\n\n/**\n * CodeCall plugin configuration schema.\n */\nexport const FrontMcpCodeCallSchema = z.object({\n /** Whether this tool can be used via CodeCall */\n enabledInCodeCall: z.boolean().optional(),\n /** If true, stays visible in list_tools when CodeCall is active */\n visibleInListTools: z.boolean().optional(),\n});\n\nexport type FrontMcpCodeCall = z.infer<typeof FrontMcpCodeCallSchema>;\n\n// ============================================================================\n// Example Schema (v1.0)\n// ============================================================================\n\n/**\n * Tool usage example schema.\n */\nexport const FrontMcpExampleSchema = z.object({\n /** Description of the example */\n description: z.string(),\n /** Example input values */\n input: z.record(z.string(), z.any()),\n /** Expected output (optional) */\n output: z.any().optional(),\n});\n\nexport type FrontMcpExample = z.infer<typeof FrontMcpExampleSchema>;\n\n// ============================================================================\n// Known Fields for Validation\n// ============================================================================\n\n/** Known field names for validation */\nconst KNOWN_EXTENSION_FIELDS = new Set([\n 'version',\n 'annotations',\n 'cache',\n 'codecall',\n 'tags',\n 'hideFromDiscovery',\n 'examples',\n]);\n\nconst KNOWN_ANNOTATION_FIELDS = new Set([\n 'title',\n 'readOnlyHint',\n 'destructiveHint',\n 'idempotentHint',\n 'openWorldHint',\n]);\n\nconst KNOWN_CACHE_FIELDS = new Set(['ttl', 'slideWindow']);\nconst KNOWN_CODECALL_FIELDS = new Set(['enabledInCodeCall', 'visibleInListTools']);\n\n// ============================================================================\n// Validated Extension Type (after parsing)\n// ============================================================================\n\n/**\n * Validated x-frontmcp extension data.\n * This is the output after schema validation.\n */\nexport interface ValidatedFrontMcpExtension {\n version: FrontMcpSchemaVersion;\n annotations?: FrontMcpAnnotations;\n cache?: FrontMcpCache;\n codecall?: FrontMcpCodeCall;\n tags?: string[];\n hideFromDiscovery?: boolean;\n examples?: FrontMcpExample[];\n}\n\n// ============================================================================\n// Validation Result\n// ============================================================================\n\nexport interface FrontMcpValidationResult {\n /** Whether validation succeeded (may still have warnings) */\n success: boolean;\n /** Validated data (only valid fields) */\n data: ValidatedFrontMcpExtension | null;\n /** Validation warnings (invalid fields that were ignored) */\n warnings: string[];\n}\n\n// ============================================================================\n// Schema Validation Functions\n// ============================================================================\n\n/**\n * Validate and parse x-frontmcp extension data.\n *\n * This function:\n * 1. Detects the schema version (defaults to current)\n * 2. Validates each field against the appropriate schema\n * 3. Returns only valid fields, ignoring invalid ones\n * 4. Collects warnings for invalid/unknown fields\n *\n * @param rawData - Raw x-frontmcp data from OpenAPI spec\n * @param toolName - Tool name for error context\n * @param logger - Logger for warnings\n * @returns Validation result with valid data and warnings\n */\nexport function validateFrontMcpExtension(\n rawData: unknown,\n toolName: string,\n logger: FrontMcpLogger,\n): FrontMcpValidationResult {\n const warnings: string[] = [];\n\n // Handle null/undefined\n if (rawData === null || rawData === undefined) {\n return { success: true, data: null, warnings: [] };\n }\n\n // Must be an object\n if (typeof rawData !== 'object' || Array.isArray(rawData)) {\n warnings.push(`x-frontmcp must be an object, got ${Array.isArray(rawData) ? 'array' : typeof rawData}`);\n logger.warn(`[${toolName}] Invalid x-frontmcp extension: ${warnings[0]}`);\n return { success: false, data: null, warnings };\n }\n\n const data = rawData as Record<string, unknown>;\n\n // Detect version\n const version = typeof data['version'] === 'string' ? data['version'] : FRONTMCP_SCHEMA_VERSION;\n\n // Check if version is supported\n if (!SUPPORTED_VERSIONS.includes(version as FrontMcpSchemaVersion)) {\n warnings.push(`Unsupported x-frontmcp version '${version}'. Supported versions: ${SUPPORTED_VERSIONS.join(', ')}`);\n logger.warn(`[${toolName}] ${warnings[0]}`);\n return { success: false, data: null, warnings };\n }\n\n // Extract valid fields and collect warnings\n const validData = extractValidFields(data, version, warnings);\n\n if (warnings.length > 0) {\n logger.warn(`[${toolName}] x-frontmcp extension has invalid fields that were ignored:`);\n warnings.forEach((w) => logger.warn(` - ${w}`));\n }\n\n return {\n success: true,\n data: validData,\n warnings,\n };\n}\n\n/**\n * Extract valid fields from x-frontmcp data, collecting warnings for invalid fields.\n */\nfunction extractValidFields(\n data: Record<string, unknown>,\n version: string,\n warnings: string[],\n): ValidatedFrontMcpExtension {\n const result: ValidatedFrontMcpExtension = {\n version: version as FrontMcpSchemaVersion,\n };\n\n // Warn about unknown top-level fields\n for (const key of Object.keys(data)) {\n if (!KNOWN_EXTENSION_FIELDS.has(key)) {\n warnings.push(`Unknown field '${key}' in x-frontmcp (will be ignored)`);\n }\n }\n\n // Validate annotations\n if (data['annotations'] !== undefined) {\n const annotationsResult = FrontMcpAnnotationsSchema.safeParse(data['annotations']);\n if (annotationsResult.success) {\n result.annotations = annotationsResult.data;\n } else {\n const issues = formatZodIssues(annotationsResult.error.issues, 'annotations');\n warnings.push(...issues);\n // Try to extract valid annotation fields\n result.annotations = extractValidAnnotations(data['annotations'], warnings);\n }\n }\n\n // Validate cache\n if (data['cache'] !== undefined) {\n const cacheResult = FrontMcpCacheSchema.safeParse(data['cache']);\n if (cacheResult.success) {\n result.cache = cacheResult.data;\n } else {\n const issues = formatZodIssues(cacheResult.error.issues, 'cache');\n warnings.push(...issues);\n // Try to extract valid cache fields\n result.cache = extractValidCache(data['cache'], warnings);\n }\n }\n\n // Validate codecall\n if (data['codecall'] !== undefined) {\n const codecallResult = FrontMcpCodeCallSchema.safeParse(data['codecall']);\n if (codecallResult.success) {\n result.codecall = codecallResult.data;\n } else {\n const issues = formatZodIssues(codecallResult.error.issues, 'codecall');\n warnings.push(...issues);\n // Try to extract valid codecall fields\n result.codecall = extractValidCodeCall(data['codecall'], warnings);\n }\n }\n\n // Validate tags\n if (data['tags'] !== undefined) {\n const tagsSchema = z.array(z.string());\n const tagsResult = tagsSchema.safeParse(data['tags']);\n if (tagsResult.success) {\n result.tags = tagsResult.data;\n } else {\n warnings.push(`Invalid 'tags': expected array of strings`);\n // Try to extract valid tags\n result.tags = extractValidTags(data['tags']);\n }\n }\n\n // Validate hideFromDiscovery\n if (data['hideFromDiscovery'] !== undefined) {\n if (typeof data['hideFromDiscovery'] === 'boolean') {\n result.hideFromDiscovery = data['hideFromDiscovery'];\n } else {\n warnings.push(`Invalid 'hideFromDiscovery': expected boolean, got ${typeof data['hideFromDiscovery']}`);\n }\n }\n\n // Validate examples\n if (data['examples'] !== undefined) {\n const examplesSchema = z.array(FrontMcpExampleSchema);\n const examplesResult = examplesSchema.safeParse(data['examples']);\n if (examplesResult.success) {\n result.examples = examplesResult.data;\n } else {\n warnings.push(`Invalid 'examples': some examples have invalid format`);\n // Try to extract valid examples\n result.examples = extractValidExamples(data['examples'], warnings);\n }\n }\n\n return result;\n}\n\n/**\n * Extract valid annotation fields.\n */\nfunction extractValidAnnotations(data: unknown, warnings: string[]): FrontMcpAnnotations | undefined {\n if (typeof data !== 'object' || data === null) return undefined;\n\n const obj = data as Record<string, unknown>;\n const result: FrontMcpAnnotations = {};\n\n for (const field of KNOWN_ANNOTATION_FIELDS) {\n if (obj[field] !== undefined) {\n if (field === 'title' && typeof obj[field] === 'string') {\n result.title = obj[field] as string;\n } else if (field !== 'title' && typeof obj[field] === 'boolean') {\n (result as Record<string, unknown>)[field] = obj[field];\n }\n }\n }\n\n // Warn about unknown annotation fields\n for (const key of Object.keys(obj)) {\n if (!KNOWN_ANNOTATION_FIELDS.has(key)) {\n warnings.push(`Unknown field 'annotations.${key}' (will be ignored)`);\n }\n }\n\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\n/**\n * Extract valid cache fields.\n */\nfunction extractValidCache(data: unknown, warnings: string[]): FrontMcpCache | undefined {\n if (typeof data !== 'object' || data === null) return undefined;\n\n const obj = data as Record<string, unknown>;\n const result: FrontMcpCache = {};\n\n if (typeof obj['ttl'] === 'number' && Number.isInteger(obj['ttl']) && obj['ttl'] > 0) {\n result.ttl = obj['ttl'];\n } else if (obj['ttl'] !== undefined) {\n warnings.push(`Invalid 'cache.ttl': expected positive integer`);\n }\n\n if (typeof obj['slideWindow'] === 'boolean') {\n result.slideWindow = obj['slideWindow'];\n } else if (obj['slideWindow'] !== undefined) {\n warnings.push(`Invalid 'cache.slideWindow': expected boolean`);\n }\n\n // Warn about unknown cache fields\n for (const key of Object.keys(obj)) {\n if (!KNOWN_CACHE_FIELDS.has(key)) {\n warnings.push(`Unknown field 'cache.${key}' (will be ignored)`);\n }\n }\n\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\n/**\n * Extract valid codecall fields.\n */\nfunction extractValidCodeCall(data: unknown, warnings: string[]): FrontMcpCodeCall | undefined {\n if (typeof data !== 'object' || data === null) return undefined;\n\n const obj = data as Record<string, unknown>;\n const result: FrontMcpCodeCall = {};\n\n if (typeof obj['enabledInCodeCall'] === 'boolean') {\n result.enabledInCodeCall = obj['enabledInCodeCall'];\n } else if (obj['enabledInCodeCall'] !== undefined) {\n warnings.push(`Invalid 'codecall.enabledInCodeCall': expected boolean`);\n }\n\n if (typeof obj['visibleInListTools'] === 'boolean') {\n result.visibleInListTools = obj['visibleInListTools'];\n } else if (obj['visibleInListTools'] !== undefined) {\n warnings.push(`Invalid 'codecall.visibleInListTools': expected boolean`);\n }\n\n // Warn about unknown codecall fields\n for (const key of Object.keys(obj)) {\n if (!KNOWN_CODECALL_FIELDS.has(key)) {\n warnings.push(`Unknown field 'codecall.${key}' (will be ignored)`);\n }\n }\n\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\n/**\n * Extract valid tags from array.\n */\nfunction extractValidTags(data: unknown): string[] | undefined {\n if (!Array.isArray(data)) return undefined;\n\n const validTags = data.filter((item): item is string => typeof item === 'string');\n return validTags.length > 0 ? validTags : undefined;\n}\n\n/**\n * Extract valid examples from array.\n */\nfunction extractValidExamples(data: unknown, warnings: string[]): FrontMcpExample[] | undefined {\n if (!Array.isArray(data)) return undefined;\n\n const validExamples: FrontMcpExample[] = [];\n\n for (let i = 0; i < data.length; i++) {\n const item = data[i];\n const result = FrontMcpExampleSchema.safeParse(item);\n if (result.success) {\n validExamples.push(result.data);\n } else {\n warnings.push(\n `Invalid example at index ${i}: ${formatZodIssues(result.error.issues, `examples[${i}]`).join(', ')}`,\n );\n }\n }\n\n return validExamples.length > 0 ? validExamples : undefined;\n}\n\n/** Zod issue shape for formatting */\ninterface ZodIssue {\n path: PropertyKey[];\n message: string;\n}\n\n/**\n * Format Zod validation issues into readable messages.\n */\nfunction formatZodIssues(issues: ZodIssue[], prefix: string): string[] {\n return issues.map((issue) => {\n const path = issue.path.length > 0 ? `${prefix}.${issue.path.join('.')}` : prefix;\n return `Invalid '${path}': ${issue.message}`;\n });\n}\n"]}
@@ -1,242 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createSecurityContextFromAuth = createSecurityContextFromAuth;
4
- exports.extractSecuritySchemes = extractSecuritySchemes;
5
- exports.validateSecurityConfiguration = validateSecurityConfiguration;
6
- exports.resolveToolSecurity = resolveToolSecurity;
7
- const mcp_from_openapi_1 = require("mcp-from-openapi");
8
- /**
9
- * Resolve security context from FrontMCP context with support for multiple auth providers
10
- *
11
- * @param tool - OpenAPI tool to resolve security for
12
- * @param ctx - FrontMCP request context with authInfo, sessionId, traceId, etc.
13
- * @param options - Adapter options with auth configuration
14
- * @returns Security context for resolver
15
- */
16
- async function createSecurityContextFromAuth(tool, ctx, options) {
17
- // 1. Use custom security resolver if provided (highest priority)
18
- if (options.securityResolver) {
19
- return await options.securityResolver(tool, ctx);
20
- }
21
- // 2. Use auth provider mapper if provided
22
- if (options.authProviderMapper) {
23
- const context = (0, mcp_from_openapi_1.createSecurityContext)({});
24
- // Find all security schemes used by this tool
25
- const securitySchemes = new Set();
26
- for (const mapper of tool.mapper) {
27
- if (mapper.security?.scheme) {
28
- securitySchemes.add(mapper.security.scheme);
29
- }
30
- }
31
- // Map each security scheme to its auth provider
32
- // Process all schemes - first matching token for each auth type (jwt, apiKey, basic, oauth2Token)
33
- for (const scheme of securitySchemes) {
34
- const authExtractor = options.authProviderMapper[scheme];
35
- if (authExtractor) {
36
- try {
37
- const token = authExtractor(ctx);
38
- // Validate return type - must be string or undefined/null
39
- if (token !== undefined && token !== null && typeof token !== 'string') {
40
- throw new Error(`authProviderMapper['${scheme}'] must return a string or undefined, ` + `but returned: ${typeof token}`);
41
- }
42
- // Reject empty string tokens explicitly - indicates misconfiguration
43
- if (token === '') {
44
- throw new Error(`authProviderMapper['${scheme}'] returned empty string. ` +
45
- `Return undefined/null if no token is available, or provide a valid token.`);
46
- }
47
- if (token) {
48
- // Route token to correct context field based on scheme type
49
- // Look up the scheme info from the mapper to determine type
50
- const schemeMapper = tool.mapper.find((m) => m.security?.scheme === scheme);
51
- const schemeType = schemeMapper?.security?.type?.toLowerCase();
52
- const httpScheme = schemeMapper?.security?.httpScheme?.toLowerCase();
53
- // Route based on security scheme type (first token for each type wins)
54
- if (schemeType === 'apikey') {
55
- if (!context.apiKey) {
56
- context.apiKey = token;
57
- }
58
- }
59
- else if (schemeType === 'http' && httpScheme === 'basic') {
60
- if (!context.basic) {
61
- context.basic = token;
62
- }
63
- }
64
- else if (schemeType === 'oauth2') {
65
- if (!context.oauth2Token) {
66
- context.oauth2Token = token;
67
- }
68
- }
69
- else {
70
- // Default to jwt for http bearer and unknown types
71
- if (!context.jwt) {
72
- context.jwt = token;
73
- }
74
- }
75
- // Continue checking other schemes - don't break
76
- // This allows validation to see all configured providers
77
- }
78
- }
79
- catch (err) {
80
- // Re-throw validation errors as-is
81
- if (err instanceof Error && err.message.includes('authProviderMapper')) {
82
- throw err;
83
- }
84
- // Wrap other errors with context
85
- const errorMessage = err instanceof Error ? err.message : String(err);
86
- throw new Error(`authProviderMapper['${scheme}'] threw an error: ${errorMessage}`);
87
- }
88
- }
89
- }
90
- // If no auth was set from providers, fall back to ctx.authInfo.token
91
- // Only fall back if ALL auth fields are empty (not just jwt)
92
- const hasAnyAuth = context.jwt || context.apiKey || context.basic || context.oauth2Token;
93
- const authToken = ctx.authInfo?.token;
94
- if (!hasAnyAuth && authToken) {
95
- // Validate type before assignment to prevent non-string values
96
- if (typeof authToken !== 'string') {
97
- throw new Error(`authInfo.token must be a string, but got: ${typeof authToken}`);
98
- }
99
- context.jwt = authToken;
100
- }
101
- return context;
102
- }
103
- // 3. Use static auth if provided
104
- if (options.staticAuth) {
105
- return (0, mcp_from_openapi_1.createSecurityContext)(options.staticAuth);
106
- }
107
- // 4. Default: use main JWT token from auth context
108
- return (0, mcp_from_openapi_1.createSecurityContext)({
109
- jwt: ctx.authInfo?.token,
110
- });
111
- }
112
- /**
113
- * Extract all security schemes used by a set of tools
114
- *
115
- * @param tools - OpenAPI tools
116
- * @returns Set of security scheme names
117
- */
118
- function extractSecuritySchemes(tools) {
119
- const schemes = new Set();
120
- for (const tool of tools) {
121
- for (const mapper of tool.mapper) {
122
- if (mapper.security?.scheme) {
123
- schemes.add(mapper.security.scheme);
124
- }
125
- }
126
- }
127
- return schemes;
128
- }
129
- /**
130
- * Validate security configuration against OpenAPI security requirements
131
- *
132
- * @param tools - OpenAPI tools
133
- * @param options - Adapter options
134
- * @returns Validation result with errors and warnings
135
- */
136
- function validateSecurityConfiguration(tools, options) {
137
- const result = {
138
- valid: true,
139
- missingMappings: [],
140
- warnings: [],
141
- securityRiskScore: 'low',
142
- };
143
- // Extract all security schemes used
144
- const securitySchemes = extractSecuritySchemes(tools);
145
- // If includeSecurityInInput is true, auth is provided by user (high security risk)
146
- const includeSecurityInInput = options.generateOptions?.includeSecurityInInput ?? false;
147
- if (includeSecurityInInput) {
148
- result.securityRiskScore = 'high';
149
- result.warnings.push('SECURITY WARNING: includeSecurityInInput is enabled. Users will provide authentication directly in tool inputs. This increases security risk as credentials may be logged or exposed.');
150
- // Don't validate mappings if security is in input
151
- return result;
152
- }
153
- // Check if we have custom security resolver (most flexible, low risk)
154
- if (options.securityResolver) {
155
- result.securityRiskScore = 'low';
156
- result.warnings.push('INFO: Using custom securityResolver. Ensure your resolver properly validates and secures credentials from context.');
157
- return result;
158
- }
159
- // Check if we have static auth (medium risk - static credentials)
160
- if (options.staticAuth && Object.keys(options.staticAuth).length > 0) {
161
- result.securityRiskScore = 'medium';
162
- result.warnings.push('SECURITY INFO: Using staticAuth with hardcoded credentials. Ensure credentials are stored securely (environment variables, secrets manager).');
163
- // If static auth is provided, assume it covers all schemes
164
- return result;
165
- }
166
- // Get schemes that will be provided via input (don't need mapping)
167
- const schemesInInput = new Set(options.securitySchemesInInput || []);
168
- // Check authProviderMapper (low risk - context-based auth)
169
- if (options.authProviderMapper || schemesInInput.size > 0) {
170
- result.securityRiskScore = schemesInInput.size > 0 ? 'medium' : 'low';
171
- // Log info about per-scheme control
172
- if (schemesInInput.size > 0) {
173
- result.warnings.push(`INFO: Per-scheme security control enabled. Schemes in input: ${Array.from(schemesInInput).join(', ')}`);
174
- }
175
- // Validate that all schemes have mappings (except those in input)
176
- for (const scheme of securitySchemes) {
177
- // Skip schemes that will be provided via input
178
- if (schemesInInput.has(scheme)) {
179
- continue;
180
- }
181
- // Check if there's a mapping for this scheme
182
- if (!options.authProviderMapper?.[scheme]) {
183
- result.valid = false;
184
- result.missingMappings.push(scheme);
185
- }
186
- }
187
- if (!result.valid) {
188
- result.warnings.push(`ERROR: Missing auth provider mappings for security schemes: ${result.missingMappings.join(', ')}`);
189
- }
190
- return result;
191
- }
192
- // No auth configuration provided - will use default ctx.authInfo.token
193
- // This only works if there's a single Bearer auth scheme
194
- if (securitySchemes.size > 0) {
195
- result.securityRiskScore = 'medium';
196
- result.warnings.push(`INFO: No auth configuration provided. Using default ctx.authInfo.token for all security schemes: ${Array.from(securitySchemes).join(', ')}`);
197
- result.warnings.push('RECOMMENDATION: For multiple auth providers, use authProviderMapper or securityResolver to map each security scheme to the correct auth provider.');
198
- }
199
- return result;
200
- }
201
- /**
202
- * Resolve security for an OpenAPI tool with validation
203
- *
204
- * @param tool - OpenAPI tool with mapper
205
- * @param ctx - FrontMCP request context with authInfo, sessionId, traceId, etc.
206
- * @param options - Adapter options with auth configuration
207
- * @returns Resolved security (headers, query params, etc.)
208
- * @throws Error if security cannot be resolved
209
- */
210
- async function resolveToolSecurity(tool, ctx, options) {
211
- const securityResolver = new mcp_from_openapi_1.SecurityResolver();
212
- const securityContext = await createSecurityContextFromAuth(tool, ctx, options);
213
- // Validate that we have auth for this tool
214
- const hasAuth = securityContext.jwt ||
215
- securityContext.apiKey ||
216
- securityContext.basic ||
217
- securityContext.oauth2Token ||
218
- (securityContext.apiKeys && Object.keys(securityContext.apiKeys).length > 0) ||
219
- (securityContext.customHeaders && Object.keys(securityContext.customHeaders).length > 0);
220
- // Check if this tool requires security
221
- // A tool requires security ONLY if a mapper has security with required=true
222
- // Optional security schemes (required=false or undefined) should not block requests
223
- const requiresSecurity = tool.mapper.some((m) => m.security && m.required === true);
224
- if (requiresSecurity && !hasAuth) {
225
- // Extract required security scheme names for error message
226
- const requiredSchemes = tool.mapper
227
- .filter((m) => m.security && m.required === true)
228
- .map((m) => m.security?.scheme ?? 'unknown');
229
- const uniqueSchemes = [...new Set(requiredSchemes)];
230
- const schemesStr = uniqueSchemes.join(', ') || 'unknown';
231
- const firstScheme = uniqueSchemes[0] || 'BearerAuth';
232
- throw new Error(`Authentication required for tool '${tool.name}' but no auth configuration found.\n` +
233
- `Required security schemes: ${schemesStr}\n` +
234
- `Solutions:\n` +
235
- ` 1. Add authProviderMapper: { '${firstScheme}': (ctx) => ctx.authInfo.user?.token }\n` +
236
- ` 2. Add securityResolver: (tool, ctx) => ({ jwt: ctx.authInfo.token })\n` +
237
- ` 3. Add staticAuth: { jwt: process.env.API_TOKEN }\n` +
238
- ` 4. Set generateOptions.includeSecurityInInput: true (not recommended for production)`);
239
- }
240
- return await securityResolver.resolve(tool.mapper, securityContext);
241
- }
242
- //# sourceMappingURL=openapi.security.js.map