@frontmcp/adapters 1.0.4 → 1.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/index.mjs +403 -403
- package/esm/openapi/index.mjs +403 -403
- package/esm/package.json +6 -4
- package/index.js +407 -407
- package/openapi/index.js +407 -407
- package/openapi/openapi.frontmcp-schema.d.ts +21 -21
- package/openapi/openapi.frontmcp-schema.d.ts.map +1 -1
- package/openapi/openapi.tool.d.ts +2 -2
- package/openapi/openapi.tool.d.ts.map +1 -1
- package/package.json +6 -4
package/openapi/index.js
CHANGED
|
@@ -42,156 +42,248 @@ var import_sdk4 = require("@frontmcp/sdk");
|
|
|
42
42
|
var import_mcp_from_openapi2 = require("mcp-from-openapi");
|
|
43
43
|
|
|
44
44
|
// libs/adapters/src/openapi/openapi.tool.ts
|
|
45
|
-
var import_zod2 = require("zod");
|
|
46
|
-
var import_sdk2 = require("@frontmcp/sdk");
|
|
47
45
|
var import_zod_from_json_schema = require("zod-from-json-schema");
|
|
46
|
+
var import_lazy_zod2 = require("@frontmcp/lazy-zod");
|
|
47
|
+
var import_sdk2 = require("@frontmcp/sdk");
|
|
48
48
|
|
|
49
|
-
// libs/adapters/src/openapi/openapi.
|
|
50
|
-
var
|
|
51
|
-
var
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
// libs/adapters/src/openapi/openapi.frontmcp-schema.ts
|
|
50
|
+
var import_lazy_zod = require("@frontmcp/lazy-zod");
|
|
51
|
+
var FRONTMCP_SCHEMA_VERSION = "1.0";
|
|
52
|
+
var SUPPORTED_VERSIONS = ["1.0"];
|
|
53
|
+
var FrontMcpAnnotationsSchema = import_lazy_zod.z.object({
|
|
54
|
+
/** Human-readable title for the tool */
|
|
55
|
+
title: import_lazy_zod.z.string().optional(),
|
|
56
|
+
/** If true, the tool does not modify its environment */
|
|
57
|
+
readOnlyHint: import_lazy_zod.z.boolean().optional(),
|
|
58
|
+
/** If true, the tool may perform destructive updates */
|
|
59
|
+
destructiveHint: import_lazy_zod.z.boolean().optional(),
|
|
60
|
+
/** If true, calling repeatedly with same args has no additional effect */
|
|
61
|
+
idempotentHint: import_lazy_zod.z.boolean().optional(),
|
|
62
|
+
/** If true, tool may interact with external entities */
|
|
63
|
+
openWorldHint: import_lazy_zod.z.boolean().optional()
|
|
64
|
+
});
|
|
65
|
+
var FrontMcpCacheSchema = import_lazy_zod.z.object({
|
|
66
|
+
/** Time-to-live in seconds for cached responses */
|
|
67
|
+
ttl: import_lazy_zod.z.number().int().positive().optional(),
|
|
68
|
+
/** If true, cache window slides on each access */
|
|
69
|
+
slideWindow: import_lazy_zod.z.boolean().optional()
|
|
70
|
+
});
|
|
71
|
+
var FrontMcpCodeCallSchema = import_lazy_zod.z.object({
|
|
72
|
+
/** Whether this tool can be used via CodeCall */
|
|
73
|
+
enabledInCodeCall: import_lazy_zod.z.boolean().optional(),
|
|
74
|
+
/** If true, stays visible in list_tools when CodeCall is active */
|
|
75
|
+
visibleInListTools: import_lazy_zod.z.boolean().optional()
|
|
76
|
+
});
|
|
77
|
+
var FrontMcpExampleSchema = import_lazy_zod.z.object({
|
|
78
|
+
/** Description of the example */
|
|
79
|
+
description: import_lazy_zod.z.string(),
|
|
80
|
+
/** Example input values */
|
|
81
|
+
input: import_lazy_zod.z.record(import_lazy_zod.z.string(), import_lazy_zod.z.any()),
|
|
82
|
+
/** Expected output (optional) */
|
|
83
|
+
output: import_lazy_zod.z.any().optional()
|
|
84
|
+
});
|
|
85
|
+
var KNOWN_EXTENSION_FIELDS = /* @__PURE__ */ new Set([
|
|
86
|
+
"version",
|
|
87
|
+
"annotations",
|
|
88
|
+
"cache",
|
|
89
|
+
"codecall",
|
|
90
|
+
"tags",
|
|
91
|
+
"hideFromDiscovery",
|
|
92
|
+
"examples"
|
|
93
|
+
]);
|
|
94
|
+
var KNOWN_ANNOTATION_FIELDS = /* @__PURE__ */ new Set([
|
|
95
|
+
"title",
|
|
96
|
+
"readOnlyHint",
|
|
97
|
+
"destructiveHint",
|
|
98
|
+
"idempotentHint",
|
|
99
|
+
"openWorldHint"
|
|
100
|
+
]);
|
|
101
|
+
var KNOWN_CACHE_FIELDS = /* @__PURE__ */ new Set(["ttl", "slideWindow"]);
|
|
102
|
+
var KNOWN_CODECALL_FIELDS = /* @__PURE__ */ new Set(["enabledInCodeCall", "visibleInListTools"]);
|
|
103
|
+
function validateFrontMcpExtension(rawData, toolName, logger) {
|
|
104
|
+
const warnings = [];
|
|
105
|
+
if (rawData === null || rawData === void 0) {
|
|
106
|
+
return { success: true, data: null, warnings: [] };
|
|
55
107
|
}
|
|
56
|
-
if (typeof
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
throw new Error(`${location} parameter '${paramName}' cannot be an array. Received: ${JSON.stringify(value)}`);
|
|
62
|
-
}
|
|
63
|
-
throw new Error(`${location} parameter '${paramName}' cannot be an object. Received: ${JSON.stringify(value)}`);
|
|
108
|
+
if (typeof rawData !== "object" || Array.isArray(rawData)) {
|
|
109
|
+
warnings.push(`x-frontmcp must be an object, got ${Array.isArray(rawData) ? "array" : typeof rawData}`);
|
|
110
|
+
logger.warn(`[${toolName}] Invalid x-frontmcp extension: ${warnings[0]}`);
|
|
111
|
+
return { success: false, data: null, warnings };
|
|
64
112
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
113
|
+
const data = rawData;
|
|
114
|
+
const version = typeof data["version"] === "string" ? data["version"] : FRONTMCP_SCHEMA_VERSION;
|
|
115
|
+
if (!SUPPORTED_VERSIONS.includes(version)) {
|
|
116
|
+
warnings.push(`Unsupported x-frontmcp version '${version}'. Supported versions: ${SUPPORTED_VERSIONS.join(", ")}`);
|
|
117
|
+
logger.warn(`[${toolName}] ${warnings[0]}`);
|
|
118
|
+
return { success: false, data: null, warnings };
|
|
70
119
|
}
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
120
|
+
const validData = extractValidFields(data, version, warnings);
|
|
121
|
+
if (warnings.length > 0) {
|
|
122
|
+
logger.warn(`[${toolName}] x-frontmcp extension has invalid fields that were ignored:`);
|
|
123
|
+
warnings.forEach((w) => logger.warn(` - ${w}`));
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
success: true,
|
|
127
|
+
data: validData,
|
|
128
|
+
warnings
|
|
129
|
+
};
|
|
75
130
|
}
|
|
76
|
-
function
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
accept: "application/json",
|
|
84
|
-
...security.headers
|
|
85
|
-
});
|
|
86
|
-
let body;
|
|
87
|
-
for (const mapper of tool2.mapper) {
|
|
88
|
-
if (mapper.security) continue;
|
|
89
|
-
const value = input[mapper.inputKey];
|
|
90
|
-
if (value === void 0 || value === null) {
|
|
91
|
-
if (mapper.required) {
|
|
92
|
-
throw new Error(
|
|
93
|
-
`Required ${mapper.type} parameter '${mapper.key}' (input: '${mapper.inputKey}') is missing for operation '${tool2.name}'`
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
continue;
|
|
131
|
+
function extractValidFields(data, version, warnings) {
|
|
132
|
+
const result = {
|
|
133
|
+
version
|
|
134
|
+
};
|
|
135
|
+
for (const key of Object.keys(data)) {
|
|
136
|
+
if (!KNOWN_EXTENSION_FIELDS.has(key)) {
|
|
137
|
+
warnings.push(`Unknown field '${key}' in x-frontmcp (will be ignored)`);
|
|
97
138
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (/[\r\n\x00\f\v]/.test(headerValue)) {
|
|
108
|
-
throw new import_sdk.PublicMcpError(
|
|
109
|
-
`Invalid header value for '${mapper.key}': contains control characters (possible header injection attack)`,
|
|
110
|
-
"INVALID_HEADER_VALUE",
|
|
111
|
-
400
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
headers.set(mapper.key, headerValue);
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
case "cookie":
|
|
118
|
-
appendCookie(headers, mapper.key, value);
|
|
119
|
-
break;
|
|
120
|
-
case "body":
|
|
121
|
-
if (!body) body = {};
|
|
122
|
-
body[mapper.key] = value;
|
|
123
|
-
break;
|
|
124
|
-
default:
|
|
125
|
-
throw new Error(
|
|
126
|
-
`Unknown mapper type '${mapper.type}' for parameter '${mapper.key}' in operation '${tool2.name}'`
|
|
127
|
-
);
|
|
139
|
+
}
|
|
140
|
+
if (data["annotations"] !== void 0) {
|
|
141
|
+
const annotationsResult = FrontMcpAnnotationsSchema.safeParse(data["annotations"]);
|
|
142
|
+
if (annotationsResult.success) {
|
|
143
|
+
result.annotations = annotationsResult.data;
|
|
144
|
+
} else {
|
|
145
|
+
const issues = formatZodIssues(annotationsResult.error.issues, "annotations");
|
|
146
|
+
warnings.push(...issues);
|
|
147
|
+
result.annotations = extractValidAnnotations(data["annotations"], warnings);
|
|
128
148
|
}
|
|
129
149
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
150
|
+
if (data["cache"] !== void 0) {
|
|
151
|
+
const cacheResult = FrontMcpCacheSchema.safeParse(data["cache"]);
|
|
152
|
+
if (cacheResult.success) {
|
|
153
|
+
result.cache = cacheResult.data;
|
|
154
|
+
} else {
|
|
155
|
+
const issues = formatZodIssues(cacheResult.error.issues, "cache");
|
|
156
|
+
warnings.push(...issues);
|
|
157
|
+
result.cache = extractValidCache(data["cache"], warnings);
|
|
135
158
|
}
|
|
136
|
-
queryParams.set(key, coerceToString(value, key, "security query"));
|
|
137
|
-
});
|
|
138
|
-
if (security.cookies && Object.keys(security.cookies).length > 0) {
|
|
139
|
-
Object.entries(security.cookies).forEach(([key, value]) => {
|
|
140
|
-
appendCookie(headers, key, value);
|
|
141
|
-
});
|
|
142
159
|
}
|
|
143
|
-
if (
|
|
144
|
-
|
|
160
|
+
if (data["codecall"] !== void 0) {
|
|
161
|
+
const codecallResult = FrontMcpCodeCallSchema.safeParse(data["codecall"]);
|
|
162
|
+
if (codecallResult.success) {
|
|
163
|
+
result.codecall = codecallResult.data;
|
|
164
|
+
} else {
|
|
165
|
+
const issues = formatZodIssues(codecallResult.error.issues, "codecall");
|
|
166
|
+
warnings.push(...issues);
|
|
167
|
+
result.codecall = extractValidCodeCall(data["codecall"], warnings);
|
|
168
|
+
}
|
|
145
169
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
170
|
+
if (data["tags"] !== void 0) {
|
|
171
|
+
const tagsSchema = import_lazy_zod.z.array(import_lazy_zod.z.string());
|
|
172
|
+
const tagsResult = tagsSchema.safeParse(data["tags"]);
|
|
173
|
+
if (tagsResult.success) {
|
|
174
|
+
result.tags = tagsResult.data;
|
|
175
|
+
} else {
|
|
176
|
+
warnings.push(`Invalid 'tags': expected array of strings`);
|
|
177
|
+
result.tags = extractValidTags(data["tags"]);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (data["hideFromDiscovery"] !== void 0) {
|
|
181
|
+
if (typeof data["hideFromDiscovery"] === "boolean") {
|
|
182
|
+
result.hideFromDiscovery = data["hideFromDiscovery"];
|
|
183
|
+
} else {
|
|
184
|
+
warnings.push(`Invalid 'hideFromDiscovery': expected boolean, got ${typeof data["hideFromDiscovery"]}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (data["examples"] !== void 0) {
|
|
188
|
+
const examplesSchema = import_lazy_zod.z.array(FrontMcpExampleSchema);
|
|
189
|
+
const examplesResult = examplesSchema.safeParse(data["examples"]);
|
|
190
|
+
if (examplesResult.success) {
|
|
191
|
+
result.examples = examplesResult.data;
|
|
192
|
+
} else {
|
|
193
|
+
warnings.push(`Invalid 'examples': some examples have invalid format`);
|
|
194
|
+
result.examples = extractValidExamples(data["examples"], warnings);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return result;
|
|
149
198
|
}
|
|
150
|
-
function
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
199
|
+
function extractValidAnnotations(data, warnings) {
|
|
200
|
+
if (typeof data !== "object" || data === null) return void 0;
|
|
201
|
+
const obj = data;
|
|
202
|
+
const result = {};
|
|
203
|
+
for (const field of KNOWN_ANNOTATION_FIELDS) {
|
|
204
|
+
if (obj[field] !== void 0) {
|
|
205
|
+
if (field === "title" && typeof obj[field] === "string") {
|
|
206
|
+
result.title = obj[field];
|
|
207
|
+
} else if (field !== "title" && typeof obj[field] === "boolean") {
|
|
208
|
+
result[field] = obj[field];
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
for (const key of Object.keys(obj)) {
|
|
213
|
+
if (!KNOWN_ANNOTATION_FIELDS.has(key)) {
|
|
214
|
+
warnings.push(`Unknown field 'annotations.${key}' (will be ignored)`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
155
218
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
if (
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
219
|
+
function extractValidCache(data, warnings) {
|
|
220
|
+
if (typeof data !== "object" || data === null) return void 0;
|
|
221
|
+
const obj = data;
|
|
222
|
+
const result = {};
|
|
223
|
+
if (typeof obj["ttl"] === "number" && Number.isInteger(obj["ttl"]) && obj["ttl"] > 0) {
|
|
224
|
+
result.ttl = obj["ttl"];
|
|
225
|
+
} else if (obj["ttl"] !== void 0) {
|
|
226
|
+
warnings.push(`Invalid 'cache.ttl': expected positive integer`);
|
|
227
|
+
}
|
|
228
|
+
if (typeof obj["slideWindow"] === "boolean") {
|
|
229
|
+
result.slideWindow = obj["slideWindow"];
|
|
230
|
+
} else if (obj["slideWindow"] !== void 0) {
|
|
231
|
+
warnings.push(`Invalid 'cache.slideWindow': expected boolean`);
|
|
232
|
+
}
|
|
233
|
+
for (const key of Object.keys(obj)) {
|
|
234
|
+
if (!KNOWN_CACHE_FIELDS.has(key)) {
|
|
235
|
+
warnings.push(`Unknown field 'cache.${key}' (will be ignored)`);
|
|
164
236
|
}
|
|
165
237
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
238
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
239
|
+
}
|
|
240
|
+
function extractValidCodeCall(data, warnings) {
|
|
241
|
+
if (typeof data !== "object" || data === null) return void 0;
|
|
242
|
+
const obj = data;
|
|
243
|
+
const result = {};
|
|
244
|
+
if (typeof obj["enabledInCodeCall"] === "boolean") {
|
|
245
|
+
result.enabledInCodeCall = obj["enabledInCodeCall"];
|
|
246
|
+
} else if (obj["enabledInCodeCall"] !== void 0) {
|
|
247
|
+
warnings.push(`Invalid 'codecall.enabledInCodeCall': expected boolean`);
|
|
170
248
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
249
|
+
if (typeof obj["visibleInListTools"] === "boolean") {
|
|
250
|
+
result.visibleInListTools = obj["visibleInListTools"];
|
|
251
|
+
} else if (obj["visibleInListTools"] !== void 0) {
|
|
252
|
+
warnings.push(`Invalid 'codecall.visibleInListTools': expected boolean`);
|
|
253
|
+
}
|
|
254
|
+
for (const key of Object.keys(obj)) {
|
|
255
|
+
if (!KNOWN_CODECALL_FIELDS.has(key)) {
|
|
256
|
+
warnings.push(`Unknown field 'codecall.${key}' (will be ignored)`);
|
|
178
257
|
}
|
|
179
|
-
} else {
|
|
180
|
-
data = text;
|
|
181
258
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
259
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
260
|
+
}
|
|
261
|
+
function extractValidTags(data) {
|
|
262
|
+
if (!Array.isArray(data)) return void 0;
|
|
263
|
+
const validTags = data.filter((item) => typeof item === "string");
|
|
264
|
+
return validTags.length > 0 ? validTags : void 0;
|
|
265
|
+
}
|
|
266
|
+
function extractValidExamples(data, warnings) {
|
|
267
|
+
if (!Array.isArray(data)) return void 0;
|
|
268
|
+
const validExamples = [];
|
|
269
|
+
for (let i = 0; i < data.length; i++) {
|
|
270
|
+
const item = data[i];
|
|
271
|
+
const result = FrontMcpExampleSchema.safeParse(item);
|
|
272
|
+
if (result.success) {
|
|
273
|
+
validExamples.push(result.data);
|
|
274
|
+
} else {
|
|
275
|
+
warnings.push(
|
|
276
|
+
`Invalid example at index ${i}: ${formatZodIssues(result.error.issues, `examples[${i}]`).join(", ")}`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
189
279
|
}
|
|
190
|
-
return
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
280
|
+
return validExamples.length > 0 ? validExamples : void 0;
|
|
281
|
+
}
|
|
282
|
+
function formatZodIssues(issues, prefix) {
|
|
283
|
+
return issues.map((issue) => {
|
|
284
|
+
const path = issue.path.length > 0 ? `${prefix}.${issue.path.join(".")}` : prefix;
|
|
285
|
+
return `Invalid '${path}': ${issue.message}`;
|
|
286
|
+
});
|
|
195
287
|
}
|
|
196
288
|
|
|
197
289
|
// libs/adapters/src/openapi/openapi.security.ts
|
|
@@ -316,301 +408,209 @@ function validateSecurityConfiguration(tools, options) {
|
|
|
316
408
|
if (options.authProviderMapper || schemesInInput.size > 0) {
|
|
317
409
|
result.securityRiskScore = schemesInInput.size > 0 ? "medium" : "low";
|
|
318
410
|
if (schemesInInput.size > 0) {
|
|
319
|
-
result.warnings.push(
|
|
320
|
-
`INFO: Per-scheme security control enabled. Schemes in input: ${Array.from(schemesInInput).join(", ")}`
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
for (const scheme of securitySchemes) {
|
|
324
|
-
if (schemesInInput.has(scheme)) {
|
|
325
|
-
continue;
|
|
326
|
-
}
|
|
327
|
-
if (!options.authProviderMapper?.[scheme]) {
|
|
328
|
-
result.valid = false;
|
|
329
|
-
result.missingMappings.push(scheme);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
if (!result.valid) {
|
|
333
|
-
result.warnings.push(
|
|
334
|
-
`ERROR: Missing auth provider mappings for security schemes: ${result.missingMappings.join(", ")}`
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
return result;
|
|
338
|
-
}
|
|
339
|
-
if (securitySchemes.size > 0) {
|
|
340
|
-
result.securityRiskScore = "medium";
|
|
341
|
-
result.warnings.push(
|
|
342
|
-
`INFO: No auth configuration provided. Using default ctx.authInfo.token for all security schemes: ${Array.from(
|
|
343
|
-
securitySchemes
|
|
344
|
-
).join(", ")}`
|
|
345
|
-
);
|
|
346
|
-
result.warnings.push(
|
|
347
|
-
"RECOMMENDATION: For multiple auth providers, use authProviderMapper or securityResolver to map each security scheme to the correct auth provider."
|
|
348
|
-
);
|
|
349
|
-
}
|
|
350
|
-
return result;
|
|
351
|
-
}
|
|
352
|
-
async function resolveToolSecurity(tool2, ctx, options) {
|
|
353
|
-
const securityResolver = new import_mcp_from_openapi.SecurityResolver();
|
|
354
|
-
const securityContext = await createSecurityContextFromAuth(tool2, ctx, options);
|
|
355
|
-
const hasAuth = securityContext.jwt || securityContext.apiKey || securityContext.basic || securityContext.oauth2Token || securityContext.apiKeys && Object.keys(securityContext.apiKeys).length > 0 || securityContext.customHeaders && Object.keys(securityContext.customHeaders).length > 0;
|
|
356
|
-
const requiresSecurity = tool2.mapper.some((m) => m.security && m.required === true);
|
|
357
|
-
if (requiresSecurity && !hasAuth) {
|
|
358
|
-
const requiredSchemes = tool2.mapper.filter((m) => m.security && m.required === true).map((m) => m.security?.scheme ?? "unknown");
|
|
359
|
-
const uniqueSchemes = [...new Set(requiredSchemes)];
|
|
360
|
-
const schemesStr = uniqueSchemes.join(", ") || "unknown";
|
|
361
|
-
const firstScheme = uniqueSchemes[0] || "BearerAuth";
|
|
362
|
-
throw new Error(
|
|
363
|
-
`Authentication required for tool '${tool2.name}' but no auth configuration found.
|
|
364
|
-
Required security schemes: ${schemesStr}
|
|
365
|
-
Solutions:
|
|
366
|
-
1. Add authProviderMapper: { '${firstScheme}': (ctx) => ctx.authInfo.user?.token }
|
|
367
|
-
2. Add securityResolver: (tool, ctx) => ({ jwt: ctx.authInfo.token })
|
|
368
|
-
3. Add staticAuth: { jwt: process.env.API_TOKEN }
|
|
369
|
-
4. Set generateOptions.includeSecurityInInput: true (not recommended for production)`
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
return await securityResolver.resolve(tool2.mapper, securityContext);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// libs/adapters/src/openapi/openapi.frontmcp-schema.ts
|
|
376
|
-
var import_zod = require("zod");
|
|
377
|
-
var FRONTMCP_SCHEMA_VERSION = "1.0";
|
|
378
|
-
var SUPPORTED_VERSIONS = ["1.0"];
|
|
379
|
-
var FrontMcpAnnotationsSchema = import_zod.z.object({
|
|
380
|
-
/** Human-readable title for the tool */
|
|
381
|
-
title: import_zod.z.string().optional(),
|
|
382
|
-
/** If true, the tool does not modify its environment */
|
|
383
|
-
readOnlyHint: import_zod.z.boolean().optional(),
|
|
384
|
-
/** If true, the tool may perform destructive updates */
|
|
385
|
-
destructiveHint: import_zod.z.boolean().optional(),
|
|
386
|
-
/** If true, calling repeatedly with same args has no additional effect */
|
|
387
|
-
idempotentHint: import_zod.z.boolean().optional(),
|
|
388
|
-
/** If true, tool may interact with external entities */
|
|
389
|
-
openWorldHint: import_zod.z.boolean().optional()
|
|
390
|
-
});
|
|
391
|
-
var FrontMcpCacheSchema = import_zod.z.object({
|
|
392
|
-
/** Time-to-live in seconds for cached responses */
|
|
393
|
-
ttl: import_zod.z.number().int().positive().optional(),
|
|
394
|
-
/** If true, cache window slides on each access */
|
|
395
|
-
slideWindow: import_zod.z.boolean().optional()
|
|
396
|
-
});
|
|
397
|
-
var FrontMcpCodeCallSchema = import_zod.z.object({
|
|
398
|
-
/** Whether this tool can be used via CodeCall */
|
|
399
|
-
enabledInCodeCall: import_zod.z.boolean().optional(),
|
|
400
|
-
/** If true, stays visible in list_tools when CodeCall is active */
|
|
401
|
-
visibleInListTools: import_zod.z.boolean().optional()
|
|
402
|
-
});
|
|
403
|
-
var FrontMcpExampleSchema = import_zod.z.object({
|
|
404
|
-
/** Description of the example */
|
|
405
|
-
description: import_zod.z.string(),
|
|
406
|
-
/** Example input values */
|
|
407
|
-
input: import_zod.z.record(import_zod.z.string(), import_zod.z.any()),
|
|
408
|
-
/** Expected output (optional) */
|
|
409
|
-
output: import_zod.z.any().optional()
|
|
410
|
-
});
|
|
411
|
-
var KNOWN_EXTENSION_FIELDS = /* @__PURE__ */ new Set([
|
|
412
|
-
"version",
|
|
413
|
-
"annotations",
|
|
414
|
-
"cache",
|
|
415
|
-
"codecall",
|
|
416
|
-
"tags",
|
|
417
|
-
"hideFromDiscovery",
|
|
418
|
-
"examples"
|
|
419
|
-
]);
|
|
420
|
-
var KNOWN_ANNOTATION_FIELDS = /* @__PURE__ */ new Set([
|
|
421
|
-
"title",
|
|
422
|
-
"readOnlyHint",
|
|
423
|
-
"destructiveHint",
|
|
424
|
-
"idempotentHint",
|
|
425
|
-
"openWorldHint"
|
|
426
|
-
]);
|
|
427
|
-
var KNOWN_CACHE_FIELDS = /* @__PURE__ */ new Set(["ttl", "slideWindow"]);
|
|
428
|
-
var KNOWN_CODECALL_FIELDS = /* @__PURE__ */ new Set(["enabledInCodeCall", "visibleInListTools"]);
|
|
429
|
-
function validateFrontMcpExtension(rawData, toolName, logger) {
|
|
430
|
-
const warnings = [];
|
|
431
|
-
if (rawData === null || rawData === void 0) {
|
|
432
|
-
return { success: true, data: null, warnings: [] };
|
|
433
|
-
}
|
|
434
|
-
if (typeof rawData !== "object" || Array.isArray(rawData)) {
|
|
435
|
-
warnings.push(`x-frontmcp must be an object, got ${Array.isArray(rawData) ? "array" : typeof rawData}`);
|
|
436
|
-
logger.warn(`[${toolName}] Invalid x-frontmcp extension: ${warnings[0]}`);
|
|
437
|
-
return { success: false, data: null, warnings };
|
|
438
|
-
}
|
|
439
|
-
const data = rawData;
|
|
440
|
-
const version = typeof data["version"] === "string" ? data["version"] : FRONTMCP_SCHEMA_VERSION;
|
|
441
|
-
if (!SUPPORTED_VERSIONS.includes(version)) {
|
|
442
|
-
warnings.push(`Unsupported x-frontmcp version '${version}'. Supported versions: ${SUPPORTED_VERSIONS.join(", ")}`);
|
|
443
|
-
logger.warn(`[${toolName}] ${warnings[0]}`);
|
|
444
|
-
return { success: false, data: null, warnings };
|
|
445
|
-
}
|
|
446
|
-
const validData = extractValidFields(data, version, warnings);
|
|
447
|
-
if (warnings.length > 0) {
|
|
448
|
-
logger.warn(`[${toolName}] x-frontmcp extension has invalid fields that were ignored:`);
|
|
449
|
-
warnings.forEach((w) => logger.warn(` - ${w}`));
|
|
450
|
-
}
|
|
451
|
-
return {
|
|
452
|
-
success: true,
|
|
453
|
-
data: validData,
|
|
454
|
-
warnings
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
function extractValidFields(data, version, warnings) {
|
|
458
|
-
const result = {
|
|
459
|
-
version
|
|
460
|
-
};
|
|
461
|
-
for (const key of Object.keys(data)) {
|
|
462
|
-
if (!KNOWN_EXTENSION_FIELDS.has(key)) {
|
|
463
|
-
warnings.push(`Unknown field '${key}' in x-frontmcp (will be ignored)`);
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
if (data["annotations"] !== void 0) {
|
|
467
|
-
const annotationsResult = FrontMcpAnnotationsSchema.safeParse(data["annotations"]);
|
|
468
|
-
if (annotationsResult.success) {
|
|
469
|
-
result.annotations = annotationsResult.data;
|
|
470
|
-
} else {
|
|
471
|
-
const issues = formatZodIssues(annotationsResult.error.issues, "annotations");
|
|
472
|
-
warnings.push(...issues);
|
|
473
|
-
result.annotations = extractValidAnnotations(data["annotations"], warnings);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
if (data["cache"] !== void 0) {
|
|
477
|
-
const cacheResult = FrontMcpCacheSchema.safeParse(data["cache"]);
|
|
478
|
-
if (cacheResult.success) {
|
|
479
|
-
result.cache = cacheResult.data;
|
|
480
|
-
} else {
|
|
481
|
-
const issues = formatZodIssues(cacheResult.error.issues, "cache");
|
|
482
|
-
warnings.push(...issues);
|
|
483
|
-
result.cache = extractValidCache(data["cache"], warnings);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
if (data["codecall"] !== void 0) {
|
|
487
|
-
const codecallResult = FrontMcpCodeCallSchema.safeParse(data["codecall"]);
|
|
488
|
-
if (codecallResult.success) {
|
|
489
|
-
result.codecall = codecallResult.data;
|
|
490
|
-
} else {
|
|
491
|
-
const issues = formatZodIssues(codecallResult.error.issues, "codecall");
|
|
492
|
-
warnings.push(...issues);
|
|
493
|
-
result.codecall = extractValidCodeCall(data["codecall"], warnings);
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
if (data["tags"] !== void 0) {
|
|
497
|
-
const tagsSchema = import_zod.z.array(import_zod.z.string());
|
|
498
|
-
const tagsResult = tagsSchema.safeParse(data["tags"]);
|
|
499
|
-
if (tagsResult.success) {
|
|
500
|
-
result.tags = tagsResult.data;
|
|
501
|
-
} else {
|
|
502
|
-
warnings.push(`Invalid 'tags': expected array of strings`);
|
|
503
|
-
result.tags = extractValidTags(data["tags"]);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
if (data["hideFromDiscovery"] !== void 0) {
|
|
507
|
-
if (typeof data["hideFromDiscovery"] === "boolean") {
|
|
508
|
-
result.hideFromDiscovery = data["hideFromDiscovery"];
|
|
509
|
-
} else {
|
|
510
|
-
warnings.push(`Invalid 'hideFromDiscovery': expected boolean, got ${typeof data["hideFromDiscovery"]}`);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
if (data["examples"] !== void 0) {
|
|
514
|
-
const examplesSchema = import_zod.z.array(FrontMcpExampleSchema);
|
|
515
|
-
const examplesResult = examplesSchema.safeParse(data["examples"]);
|
|
516
|
-
if (examplesResult.success) {
|
|
517
|
-
result.examples = examplesResult.data;
|
|
518
|
-
} else {
|
|
519
|
-
warnings.push(`Invalid 'examples': some examples have invalid format`);
|
|
520
|
-
result.examples = extractValidExamples(data["examples"], warnings);
|
|
411
|
+
result.warnings.push(
|
|
412
|
+
`INFO: Per-scheme security control enabled. Schemes in input: ${Array.from(schemesInInput).join(", ")}`
|
|
413
|
+
);
|
|
521
414
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
for (const field of KNOWN_ANNOTATION_FIELDS) {
|
|
530
|
-
if (obj[field] !== void 0) {
|
|
531
|
-
if (field === "title" && typeof obj[field] === "string") {
|
|
532
|
-
result.title = obj[field];
|
|
533
|
-
} else if (field !== "title" && typeof obj[field] === "boolean") {
|
|
534
|
-
result[field] = obj[field];
|
|
415
|
+
for (const scheme of securitySchemes) {
|
|
416
|
+
if (schemesInInput.has(scheme)) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
if (!options.authProviderMapper?.[scheme]) {
|
|
420
|
+
result.valid = false;
|
|
421
|
+
result.missingMappings.push(scheme);
|
|
535
422
|
}
|
|
536
423
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
424
|
+
if (!result.valid) {
|
|
425
|
+
result.warnings.push(
|
|
426
|
+
`ERROR: Missing auth provider mappings for security schemes: ${result.missingMappings.join(", ")}`
|
|
427
|
+
);
|
|
541
428
|
}
|
|
429
|
+
return result;
|
|
542
430
|
}
|
|
543
|
-
|
|
431
|
+
if (securitySchemes.size > 0) {
|
|
432
|
+
result.securityRiskScore = "medium";
|
|
433
|
+
result.warnings.push(
|
|
434
|
+
`INFO: No auth configuration provided. Using default ctx.authInfo.token for all security schemes: ${Array.from(
|
|
435
|
+
securitySchemes
|
|
436
|
+
).join(", ")}`
|
|
437
|
+
);
|
|
438
|
+
result.warnings.push(
|
|
439
|
+
"RECOMMENDATION: For multiple auth providers, use authProviderMapper or securityResolver to map each security scheme to the correct auth provider."
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
return result;
|
|
544
443
|
}
|
|
545
|
-
function
|
|
546
|
-
|
|
547
|
-
const
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
444
|
+
async function resolveToolSecurity(tool2, ctx, options) {
|
|
445
|
+
const securityResolver = new import_mcp_from_openapi.SecurityResolver();
|
|
446
|
+
const securityContext = await createSecurityContextFromAuth(tool2, ctx, options);
|
|
447
|
+
const hasAuth = securityContext.jwt || securityContext.apiKey || securityContext.basic || securityContext.oauth2Token || securityContext.apiKeys && Object.keys(securityContext.apiKeys).length > 0 || securityContext.customHeaders && Object.keys(securityContext.customHeaders).length > 0;
|
|
448
|
+
const requiresSecurity = tool2.mapper.some((m) => m.security && m.required === true);
|
|
449
|
+
if (requiresSecurity && !hasAuth) {
|
|
450
|
+
const requiredSchemes = tool2.mapper.filter((m) => m.security && m.required === true).map((m) => m.security?.scheme ?? "unknown");
|
|
451
|
+
const uniqueSchemes = [...new Set(requiredSchemes)];
|
|
452
|
+
const schemesStr = uniqueSchemes.join(", ") || "unknown";
|
|
453
|
+
const firstScheme = uniqueSchemes[0] || "BearerAuth";
|
|
454
|
+
throw new Error(
|
|
455
|
+
`Authentication required for tool '${tool2.name}' but no auth configuration found.
|
|
456
|
+
Required security schemes: ${schemesStr}
|
|
457
|
+
Solutions:
|
|
458
|
+
1. Add authProviderMapper: { '${firstScheme}': (ctx) => ctx.authInfo.user?.token }
|
|
459
|
+
2. Add securityResolver: (tool, ctx) => ({ jwt: ctx.authInfo.token })
|
|
460
|
+
3. Add staticAuth: { jwt: process.env.API_TOKEN }
|
|
461
|
+
4. Set generateOptions.includeSecurityInInput: true (not recommended for production)`
|
|
462
|
+
);
|
|
553
463
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
464
|
+
return await securityResolver.resolve(tool2.mapper, securityContext);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// libs/adapters/src/openapi/openapi.utils.ts
|
|
468
|
+
var import_utils = require("@frontmcp/utils");
|
|
469
|
+
var import_sdk = require("@frontmcp/sdk");
|
|
470
|
+
function coerceToString(value, paramName, location) {
|
|
471
|
+
if (value === null || value === void 0) {
|
|
472
|
+
return "";
|
|
558
473
|
}
|
|
559
|
-
|
|
560
|
-
if (
|
|
561
|
-
|
|
474
|
+
if (typeof value === "object") {
|
|
475
|
+
if (Array.isArray(value)) {
|
|
476
|
+
if (location === "query") {
|
|
477
|
+
return value.map(String).join(",");
|
|
478
|
+
}
|
|
479
|
+
throw new Error(`${location} parameter '${paramName}' cannot be an array. Received: ${JSON.stringify(value)}`);
|
|
562
480
|
}
|
|
481
|
+
throw new Error(`${location} parameter '${paramName}' cannot be an object. Received: ${JSON.stringify(value)}`);
|
|
563
482
|
}
|
|
564
|
-
return
|
|
483
|
+
return String(value);
|
|
565
484
|
}
|
|
566
|
-
function
|
|
567
|
-
if (
|
|
568
|
-
|
|
569
|
-
const result = {};
|
|
570
|
-
if (typeof obj["enabledInCodeCall"] === "boolean") {
|
|
571
|
-
result.enabledInCodeCall = obj["enabledInCodeCall"];
|
|
572
|
-
} else if (obj["enabledInCodeCall"] !== void 0) {
|
|
573
|
-
warnings.push(`Invalid 'codecall.enabledInCodeCall': expected boolean`);
|
|
574
|
-
}
|
|
575
|
-
if (typeof obj["visibleInListTools"] === "boolean") {
|
|
576
|
-
result.visibleInListTools = obj["visibleInListTools"];
|
|
577
|
-
} else if (obj["visibleInListTools"] !== void 0) {
|
|
578
|
-
warnings.push(`Invalid 'codecall.visibleInListTools': expected boolean`);
|
|
485
|
+
function appendCookie(headers, name, value) {
|
|
486
|
+
if (!/^[\w!#$%&'*+\-.^`|~]+$/.test(name)) {
|
|
487
|
+
throw new Error(`Invalid cookie name: '${name}'. Cookie names must be valid tokens.`);
|
|
579
488
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
489
|
+
const existing = headers.get("Cookie") || "";
|
|
490
|
+
const cookiePair = `${name}=${encodeURIComponent(coerceToString(value, name, "cookie"))}`;
|
|
491
|
+
const combined = existing ? `${existing}; ${cookiePair}` : cookiePair;
|
|
492
|
+
headers.set("Cookie", combined);
|
|
493
|
+
}
|
|
494
|
+
function buildRequest(tool2, input, security, baseUrl) {
|
|
495
|
+
const rawBaseUrl = tool2.metadata.servers?.[0]?.url || baseUrl;
|
|
496
|
+
(0, import_utils.validateBaseUrl)(rawBaseUrl);
|
|
497
|
+
const apiBaseUrl = rawBaseUrl.replace(/\/+$/, "");
|
|
498
|
+
let path = tool2.metadata.path;
|
|
499
|
+
const queryParams = new URLSearchParams();
|
|
500
|
+
const headers = new Headers({
|
|
501
|
+
accept: "application/json",
|
|
502
|
+
...security.headers
|
|
503
|
+
});
|
|
504
|
+
let body;
|
|
505
|
+
for (const mapper of tool2.mapper) {
|
|
506
|
+
if (mapper.security) continue;
|
|
507
|
+
const value = input[mapper.inputKey];
|
|
508
|
+
if (value === void 0 || value === null) {
|
|
509
|
+
if (mapper.required) {
|
|
510
|
+
throw new Error(
|
|
511
|
+
`Required ${mapper.type} parameter '${mapper.key}' (input: '${mapper.inputKey}') is missing for operation '${tool2.name}'`
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
switch (mapper.type) {
|
|
517
|
+
case "path":
|
|
518
|
+
path = path.replaceAll(`{${mapper.key}}`, encodeURIComponent(coerceToString(value, mapper.key, "path")));
|
|
519
|
+
break;
|
|
520
|
+
case "query":
|
|
521
|
+
queryParams.set(mapper.key, coerceToString(value, mapper.key, "query"));
|
|
522
|
+
break;
|
|
523
|
+
case "header": {
|
|
524
|
+
const headerValue = coerceToString(value, mapper.key, "header");
|
|
525
|
+
if (/[\r\n\x00\f\v]/.test(headerValue)) {
|
|
526
|
+
throw new import_sdk.PublicMcpError(
|
|
527
|
+
`Invalid header value for '${mapper.key}': contains control characters (possible header injection attack)`,
|
|
528
|
+
"INVALID_HEADER_VALUE",
|
|
529
|
+
400
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
headers.set(mapper.key, headerValue);
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
case "cookie":
|
|
536
|
+
appendCookie(headers, mapper.key, value);
|
|
537
|
+
break;
|
|
538
|
+
case "body":
|
|
539
|
+
if (!body) body = {};
|
|
540
|
+
body[mapper.key] = value;
|
|
541
|
+
break;
|
|
542
|
+
default:
|
|
543
|
+
throw new Error(
|
|
544
|
+
`Unknown mapper type '${mapper.type}' for parameter '${mapper.key}' in operation '${tool2.name}'`
|
|
545
|
+
);
|
|
583
546
|
}
|
|
584
547
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
const validTags = data.filter((item) => typeof item === "string");
|
|
590
|
-
return validTags.length > 0 ? validTags : void 0;
|
|
591
|
-
}
|
|
592
|
-
function extractValidExamples(data, warnings) {
|
|
593
|
-
if (!Array.isArray(data)) return void 0;
|
|
594
|
-
const validExamples = [];
|
|
595
|
-
for (let i = 0; i < data.length; i++) {
|
|
596
|
-
const item = data[i];
|
|
597
|
-
const result = FrontMcpExampleSchema.safeParse(item);
|
|
598
|
-
if (result.success) {
|
|
599
|
-
validExamples.push(result.data);
|
|
600
|
-
} else {
|
|
601
|
-
warnings.push(
|
|
602
|
-
`Invalid example at index ${i}: ${formatZodIssues(result.error.issues, `examples[${i}]`).join(", ")}`
|
|
548
|
+
Object.entries(security.query).forEach(([key, value]) => {
|
|
549
|
+
if (queryParams.has(key)) {
|
|
550
|
+
throw new Error(
|
|
551
|
+
`Query parameter collision: '${key}' is provided both as user input and security parameter. This could indicate a security misconfiguration in operation '${tool2.name}'.`
|
|
603
552
|
);
|
|
604
553
|
}
|
|
554
|
+
queryParams.set(key, coerceToString(value, key, "security query"));
|
|
555
|
+
});
|
|
556
|
+
if (security.cookies && Object.keys(security.cookies).length > 0) {
|
|
557
|
+
Object.entries(security.cookies).forEach(([key, value]) => {
|
|
558
|
+
appendCookie(headers, key, value);
|
|
559
|
+
});
|
|
605
560
|
}
|
|
606
|
-
|
|
561
|
+
if (path.includes("{")) {
|
|
562
|
+
throw new Error(`Failed to resolve all path parameters in ${path} for operation ${tool2.name}`);
|
|
563
|
+
}
|
|
564
|
+
const queryString = queryParams.toString();
|
|
565
|
+
const url = `${apiBaseUrl}${path}${queryString ? `?${queryString}` : ""}`;
|
|
566
|
+
return { url, headers, body };
|
|
607
567
|
}
|
|
608
|
-
function
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
568
|
+
function applyAdditionalHeaders(headers, additionalHeaders) {
|
|
569
|
+
if (!additionalHeaders) return;
|
|
570
|
+
Object.entries(additionalHeaders).forEach(([key, value]) => {
|
|
571
|
+
headers.set(key, value);
|
|
612
572
|
});
|
|
613
573
|
}
|
|
574
|
+
var DEFAULT_MAX_RESPONSE_SIZE = 10 * 1024 * 1024;
|
|
575
|
+
async function parseResponse(response, options) {
|
|
576
|
+
const maxSize = options?.maxResponseSize ?? DEFAULT_MAX_RESPONSE_SIZE;
|
|
577
|
+
const contentLength = response.headers.get("content-length");
|
|
578
|
+
if (contentLength) {
|
|
579
|
+
const length = parseInt(contentLength, 10);
|
|
580
|
+
if (!isNaN(length) && isFinite(length) && length > maxSize) {
|
|
581
|
+
throw new Error(`Response size (${length} bytes) exceeds maximum allowed (${maxSize} bytes)`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
const text = await response.text();
|
|
585
|
+
const byteSize = new TextEncoder().encode(text).length;
|
|
586
|
+
if (byteSize > maxSize) {
|
|
587
|
+
throw new Error(`Response size (${byteSize} bytes) exceeds maximum allowed (${maxSize} bytes)`);
|
|
588
|
+
}
|
|
589
|
+
const contentType = response.headers.get("content-type");
|
|
590
|
+
let data;
|
|
591
|
+
if (contentType?.toLowerCase().includes("application/json")) {
|
|
592
|
+
try {
|
|
593
|
+
data = JSON.parse(text);
|
|
594
|
+
} catch {
|
|
595
|
+
data = text;
|
|
596
|
+
}
|
|
597
|
+
} else {
|
|
598
|
+
data = text;
|
|
599
|
+
}
|
|
600
|
+
if (!response.ok) {
|
|
601
|
+
return {
|
|
602
|
+
status: response.status,
|
|
603
|
+
ok: false,
|
|
604
|
+
data,
|
|
605
|
+
error: typeof data === "object" && data !== null && "message" in data ? String(data.message) : typeof data === "string" ? data : `HTTP ${response.status} error`
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
return {
|
|
609
|
+
status: response.status,
|
|
610
|
+
ok: true,
|
|
611
|
+
data
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
614
|
|
|
615
615
|
// libs/adapters/src/openapi/openapi.tool.ts
|
|
616
616
|
function createOpenApiTool(openapiTool, options, logger) {
|
|
@@ -874,7 +874,7 @@ async function injectTransformedValues(input, transforms, ctx) {
|
|
|
874
874
|
function getZodSchemaFromJsonSchema(jsonSchema, toolName, logger) {
|
|
875
875
|
if (typeof jsonSchema !== "object" || jsonSchema === null) {
|
|
876
876
|
logger.warn(`[${toolName}] No valid JSON schema provided, using permissive schema`);
|
|
877
|
-
return { schema:
|
|
877
|
+
return { schema: import_lazy_zod2.z.looseObject({}), conversionFailed: true, error: "No valid JSON schema" };
|
|
878
878
|
}
|
|
879
879
|
try {
|
|
880
880
|
const zodSchema = (0, import_zod_from_json_schema.convertJsonSchemaToZod)(jsonSchema);
|
|
@@ -887,7 +887,7 @@ function getZodSchemaFromJsonSchema(jsonSchema, toolName, logger) {
|
|
|
887
887
|
logger.warn(
|
|
888
888
|
`[${toolName}] Failed to generate Zod schema, using permissive schema. Tool will accept any input but may fail at API level. Error: ${errorMessage}`
|
|
889
889
|
);
|
|
890
|
-
return { schema:
|
|
890
|
+
return { schema: import_lazy_zod2.z.looseObject({}), conversionFailed: true, error: errorMessage };
|
|
891
891
|
}
|
|
892
892
|
}
|
|
893
893
|
|