@frontmcp/adapters 0.5.1 → 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.
- package/package.json +3 -3
- package/src/openapi/README.md +473 -11
- package/src/openapi/openapi.adapter.d.ts +39 -1
- package/src/openapi/openapi.adapter.js +342 -10
- package/src/openapi/openapi.adapter.js.map +1 -1
- package/src/openapi/openapi.frontmcp-schema.d.ts +91 -0
- package/src/openapi/openapi.frontmcp-schema.js +358 -0
- package/src/openapi/openapi.frontmcp-schema.js.map +1 -0
- package/src/openapi/openapi.security.d.ts +7 -7
- package/src/openapi/openapi.security.js +98 -30
- package/src/openapi/openapi.security.js.map +1 -1
- package/src/openapi/openapi.tool.d.ts +3 -1
- package/src/openapi/openapi.tool.js +218 -32
- package/src/openapi/openapi.tool.js.map +1 -1
- package/src/openapi/openapi.types.d.ts +486 -15
- package/src/openapi/openapi.types.js +26 -0
- package/src/openapi/openapi.types.js.map +1 -1
- package/src/openapi/openapi.utils.d.ts +17 -1
- package/src/openapi/openapi.utils.js +133 -30
- package/src/openapi/openapi.utils.js.map +1 -1
|
@@ -0,0 +1,358 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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,5 +1,5 @@
|
|
|
1
1
|
import { type McpOpenAPITool, type SecurityContext } from 'mcp-from-openapi';
|
|
2
|
-
import type {
|
|
2
|
+
import type { FrontMcpContext } from '@frontmcp/sdk';
|
|
3
3
|
import type { OpenApiAdapterOptions } from './openapi.types';
|
|
4
4
|
/**
|
|
5
5
|
* Security scheme information extracted from OpenAPI spec
|
|
@@ -21,14 +21,14 @@ export interface SecurityValidationResult {
|
|
|
21
21
|
securityRiskScore: 'low' | 'medium' | 'high';
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* Resolve security context from FrontMCP
|
|
24
|
+
* Resolve security context from FrontMCP context with support for multiple auth providers
|
|
25
25
|
*
|
|
26
26
|
* @param tool - OpenAPI tool to resolve security for
|
|
27
|
-
* @param
|
|
27
|
+
* @param ctx - FrontMCP request context with authInfo, sessionId, traceId, etc.
|
|
28
28
|
* @param options - Adapter options with auth configuration
|
|
29
29
|
* @returns Security context for resolver
|
|
30
30
|
*/
|
|
31
|
-
export declare function createSecurityContextFromAuth(tool: McpOpenAPITool,
|
|
31
|
+
export declare function createSecurityContextFromAuth(tool: McpOpenAPITool, ctx: FrontMcpContext, options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth'>): Promise<SecurityContext>;
|
|
32
32
|
/**
|
|
33
33
|
* Extract all security schemes used by a set of tools
|
|
34
34
|
*
|
|
@@ -43,14 +43,14 @@ export declare function extractSecuritySchemes(tools: McpOpenAPITool[]): Set<str
|
|
|
43
43
|
* @param options - Adapter options
|
|
44
44
|
* @returns Validation result with errors and warnings
|
|
45
45
|
*/
|
|
46
|
-
export declare function validateSecurityConfiguration(tools: McpOpenAPITool[], options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth' | 'generateOptions'>): SecurityValidationResult;
|
|
46
|
+
export declare function validateSecurityConfiguration(tools: McpOpenAPITool[], options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth' | 'generateOptions' | 'securitySchemesInInput'>): SecurityValidationResult;
|
|
47
47
|
/**
|
|
48
48
|
* Resolve security for an OpenAPI tool with validation
|
|
49
49
|
*
|
|
50
50
|
* @param tool - OpenAPI tool with mapper
|
|
51
|
-
* @param
|
|
51
|
+
* @param ctx - FrontMCP request context with authInfo, sessionId, traceId, etc.
|
|
52
52
|
* @param options - Adapter options with auth configuration
|
|
53
53
|
* @returns Resolved security (headers, query params, etc.)
|
|
54
54
|
* @throws Error if security cannot be resolved
|
|
55
55
|
*/
|
|
56
|
-
export declare function resolveToolSecurity(tool: McpOpenAPITool,
|
|
56
|
+
export declare function resolveToolSecurity(tool: McpOpenAPITool, ctx: FrontMcpContext, options: Pick<OpenApiAdapterOptions, 'securityResolver' | 'authProviderMapper' | 'staticAuth'>): Promise<import("mcp-from-openapi").ResolvedSecurity>;
|
|
@@ -6,17 +6,17 @@ exports.validateSecurityConfiguration = validateSecurityConfiguration;
|
|
|
6
6
|
exports.resolveToolSecurity = resolveToolSecurity;
|
|
7
7
|
const mcp_from_openapi_1 = require("mcp-from-openapi");
|
|
8
8
|
/**
|
|
9
|
-
* Resolve security context from FrontMCP
|
|
9
|
+
* Resolve security context from FrontMCP context with support for multiple auth providers
|
|
10
10
|
*
|
|
11
11
|
* @param tool - OpenAPI tool to resolve security for
|
|
12
|
-
* @param
|
|
12
|
+
* @param ctx - FrontMCP request context with authInfo, sessionId, traceId, etc.
|
|
13
13
|
* @param options - Adapter options with auth configuration
|
|
14
14
|
* @returns Security context for resolver
|
|
15
15
|
*/
|
|
16
|
-
async function createSecurityContextFromAuth(tool,
|
|
16
|
+
async function createSecurityContextFromAuth(tool, ctx, options) {
|
|
17
17
|
// 1. Use custom security resolver if provided (highest priority)
|
|
18
18
|
if (options.securityResolver) {
|
|
19
|
-
return await options.securityResolver(tool,
|
|
19
|
+
return await options.securityResolver(tool, ctx);
|
|
20
20
|
}
|
|
21
21
|
// 2. Use auth provider mapper if provided
|
|
22
22
|
if (options.authProviderMapper) {
|
|
@@ -29,21 +29,74 @@ async function createSecurityContextFromAuth(tool, authInfo, options) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
// Map each security scheme to its auth provider
|
|
32
|
+
// Process all schemes - first matching token for each auth type (jwt, apiKey, basic, oauth2Token)
|
|
32
33
|
for (const scheme of securitySchemes) {
|
|
33
34
|
const authExtractor = options.authProviderMapper[scheme];
|
|
34
35
|
if (authExtractor) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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}`);
|
|
41
87
|
}
|
|
42
88
|
}
|
|
43
89
|
}
|
|
44
|
-
// If no
|
|
45
|
-
if (
|
|
46
|
-
|
|
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;
|
|
47
100
|
}
|
|
48
101
|
return context;
|
|
49
102
|
}
|
|
@@ -53,7 +106,7 @@ async function createSecurityContextFromAuth(tool, authInfo, options) {
|
|
|
53
106
|
}
|
|
54
107
|
// 4. Default: use main JWT token from auth context
|
|
55
108
|
return (0, mcp_from_openapi_1.createSecurityContext)({
|
|
56
|
-
jwt: authInfo?.token,
|
|
109
|
+
jwt: ctx.authInfo?.token,
|
|
57
110
|
});
|
|
58
111
|
}
|
|
59
112
|
/**
|
|
@@ -110,12 +163,23 @@ function validateSecurityConfiguration(tools, options) {
|
|
|
110
163
|
// If static auth is provided, assume it covers all schemes
|
|
111
164
|
return result;
|
|
112
165
|
}
|
|
166
|
+
// Get schemes that will be provided via input (don't need mapping)
|
|
167
|
+
const schemesInInput = new Set(options.securitySchemesInInput || []);
|
|
113
168
|
// Check authProviderMapper (low risk - context-based auth)
|
|
114
|
-
if (options.authProviderMapper) {
|
|
115
|
-
result.securityRiskScore = 'low';
|
|
116
|
-
//
|
|
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)
|
|
117
176
|
for (const scheme of securitySchemes) {
|
|
118
|
-
|
|
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]) {
|
|
119
183
|
result.valid = false;
|
|
120
184
|
result.missingMappings.push(scheme);
|
|
121
185
|
}
|
|
@@ -138,14 +202,14 @@ function validateSecurityConfiguration(tools, options) {
|
|
|
138
202
|
* Resolve security for an OpenAPI tool with validation
|
|
139
203
|
*
|
|
140
204
|
* @param tool - OpenAPI tool with mapper
|
|
141
|
-
* @param
|
|
205
|
+
* @param ctx - FrontMCP request context with authInfo, sessionId, traceId, etc.
|
|
142
206
|
* @param options - Adapter options with auth configuration
|
|
143
207
|
* @returns Resolved security (headers, query params, etc.)
|
|
144
208
|
* @throws Error if security cannot be resolved
|
|
145
209
|
*/
|
|
146
|
-
async function resolveToolSecurity(tool,
|
|
210
|
+
async function resolveToolSecurity(tool, ctx, options) {
|
|
147
211
|
const securityResolver = new mcp_from_openapi_1.SecurityResolver();
|
|
148
|
-
const securityContext = await createSecurityContextFromAuth(tool,
|
|
212
|
+
const securityContext = await createSecurityContextFromAuth(tool, ctx, options);
|
|
149
213
|
// Validate that we have auth for this tool
|
|
150
214
|
const hasAuth = securityContext.jwt ||
|
|
151
215
|
securityContext.apiKey ||
|
|
@@ -154,18 +218,22 @@ async function resolveToolSecurity(tool, authInfo, options) {
|
|
|
154
218
|
(securityContext.apiKeys && Object.keys(securityContext.apiKeys).length > 0) ||
|
|
155
219
|
(securityContext.customHeaders && Object.keys(securityContext.customHeaders).length > 0);
|
|
156
220
|
// Check if this tool requires security
|
|
157
|
-
|
|
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);
|
|
158
224
|
if (requiresSecurity && !hasAuth) {
|
|
159
|
-
// Extract security scheme names
|
|
160
|
-
const
|
|
161
|
-
.filter((m) => m.security && m.required)
|
|
162
|
-
.map((m) => m.security?.scheme ?? 'unknown')
|
|
163
|
-
|
|
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';
|
|
164
232
|
throw new Error(`Authentication required for tool '${tool.name}' but no auth configuration found.\n` +
|
|
165
|
-
`Required security schemes: ${
|
|
233
|
+
`Required security schemes: ${schemesStr}\n` +
|
|
166
234
|
`Solutions:\n` +
|
|
167
|
-
` 1. Add authProviderMapper: { '${
|
|
168
|
-
` 2. Add securityResolver: (tool,
|
|
235
|
+
` 1. Add authProviderMapper: { '${firstScheme}': (ctx) => ctx.authInfo.user?.token }\n` +
|
|
236
|
+
` 2. Add securityResolver: (tool, ctx) => ({ jwt: ctx.authInfo.token })\n` +
|
|
169
237
|
` 3. Add staticAuth: { jwt: process.env.API_TOKEN }\n` +
|
|
170
238
|
` 4. Set generateOptions.includeSecurityInInput: true (not recommended for production)`);
|
|
171
239
|
}
|