@cocaxcode/api-testing-mcp 0.8.3 → 0.10.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/README.md +89 -13
- package/dist/index.js +508 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -1860,6 +1860,8 @@ function registerFlowTool(server, storage) {
|
|
|
1860
1860
|
|
|
1861
1861
|
// src/tools/utilities.ts
|
|
1862
1862
|
import { z as z8 } from "zod";
|
|
1863
|
+
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
1864
|
+
import { join as join2 } from "path";
|
|
1863
1865
|
function registerUtilityTools(server, storage) {
|
|
1864
1866
|
server.tool(
|
|
1865
1867
|
"export_curl",
|
|
@@ -2139,6 +2141,511 @@ ${curlCommand}`
|
|
|
2139
2141
|
}
|
|
2140
2142
|
}
|
|
2141
2143
|
);
|
|
2144
|
+
server.tool(
|
|
2145
|
+
"export_postman_collection",
|
|
2146
|
+
"Exporta los requests guardados como una Postman Collection v2.1 (JSON). Escribe el archivo en disco, importable directamente en Postman.",
|
|
2147
|
+
{
|
|
2148
|
+
name: z8.string().optional().describe('Nombre de la colecci\xF3n (default: "API Testing Collection")'),
|
|
2149
|
+
tag: z8.string().optional().describe("Filtrar requests por tag"),
|
|
2150
|
+
output_dir: z8.string().optional().describe("Directorio donde guardar el archivo (default: ./postman/)"),
|
|
2151
|
+
resolve_variables: z8.boolean().optional().describe("Resolver {{variables}} del entorno activo (default: false)")
|
|
2152
|
+
},
|
|
2153
|
+
async (params) => {
|
|
2154
|
+
try {
|
|
2155
|
+
const collections = await storage.listCollections(params.tag);
|
|
2156
|
+
if (collections.length === 0) {
|
|
2157
|
+
return {
|
|
2158
|
+
content: [
|
|
2159
|
+
{
|
|
2160
|
+
type: "text",
|
|
2161
|
+
text: params.tag ? `No hay requests guardados con tag '${params.tag}'.` : "No hay requests guardados en la colecci\xF3n."
|
|
2162
|
+
}
|
|
2163
|
+
]
|
|
2164
|
+
};
|
|
2165
|
+
}
|
|
2166
|
+
const resolveVars = params.resolve_variables ?? false;
|
|
2167
|
+
const variables = resolveVars ? await storage.getActiveVariables() : {};
|
|
2168
|
+
const savedRequests = [];
|
|
2169
|
+
for (const item of collections) {
|
|
2170
|
+
const saved = await storage.getCollection(item.name);
|
|
2171
|
+
if (saved) savedRequests.push(saved);
|
|
2172
|
+
}
|
|
2173
|
+
const tagged = /* @__PURE__ */ new Map();
|
|
2174
|
+
const untagged = [];
|
|
2175
|
+
for (const saved of savedRequests) {
|
|
2176
|
+
if (saved.tags && saved.tags.length > 0) {
|
|
2177
|
+
const tag = saved.tags[0];
|
|
2178
|
+
if (!tagged.has(tag)) tagged.set(tag, []);
|
|
2179
|
+
tagged.get(tag).push(saved);
|
|
2180
|
+
} else {
|
|
2181
|
+
untagged.push(saved);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
const items = [];
|
|
2185
|
+
for (const [tag, requests] of tagged) {
|
|
2186
|
+
items.push({
|
|
2187
|
+
name: tag,
|
|
2188
|
+
item: requests.map((r) => buildPostmanItem(r, variables, resolveVars))
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
for (const saved of untagged) {
|
|
2192
|
+
items.push(buildPostmanItem(saved, variables, resolveVars));
|
|
2193
|
+
}
|
|
2194
|
+
const activeVars = await storage.getActiveVariables();
|
|
2195
|
+
const collectionVars = Object.entries(activeVars).map(([key, value]) => ({
|
|
2196
|
+
key,
|
|
2197
|
+
value,
|
|
2198
|
+
type: "string"
|
|
2199
|
+
}));
|
|
2200
|
+
const collectionName = params.name ?? "API Testing Collection";
|
|
2201
|
+
const postmanCollection = {
|
|
2202
|
+
info: {
|
|
2203
|
+
name: collectionName,
|
|
2204
|
+
schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
|
2205
|
+
},
|
|
2206
|
+
item: items,
|
|
2207
|
+
variable: collectionVars
|
|
2208
|
+
};
|
|
2209
|
+
const json = JSON.stringify(postmanCollection, null, 2);
|
|
2210
|
+
const outputDir = params.output_dir ?? join2(process.cwd(), "postman");
|
|
2211
|
+
await mkdir2(outputDir, { recursive: true });
|
|
2212
|
+
const fileName = collectionName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") + ".postman_collection.json";
|
|
2213
|
+
const filePath = join2(outputDir, fileName);
|
|
2214
|
+
await writeFile2(filePath, json, "utf-8");
|
|
2215
|
+
return {
|
|
2216
|
+
content: [
|
|
2217
|
+
{
|
|
2218
|
+
type: "text",
|
|
2219
|
+
text: `Postman Collection v2.1 exportada (${savedRequests.length} requests).
|
|
2220
|
+
|
|
2221
|
+
Archivo: ${filePath}
|
|
2222
|
+
|
|
2223
|
+
Importa este archivo en Postman: File \u2192 Import \u2192 selecciona el archivo.`
|
|
2224
|
+
}
|
|
2225
|
+
]
|
|
2226
|
+
};
|
|
2227
|
+
} catch (error) {
|
|
2228
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2229
|
+
return {
|
|
2230
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
2231
|
+
isError: true
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
);
|
|
2236
|
+
server.tool(
|
|
2237
|
+
"import_postman_collection",
|
|
2238
|
+
"Importa una Postman Collection v2.1 (JSON) como requests guardados en la colecci\xF3n. Soporta folders, auth, headers, body y query params.",
|
|
2239
|
+
{
|
|
2240
|
+
file: z8.string().describe("Ruta al archivo .postman_collection.json exportado de Postman"),
|
|
2241
|
+
tag: z8.string().optional().describe("Tag adicional para aplicar a todos los requests importados"),
|
|
2242
|
+
overwrite: z8.boolean().optional().describe("Sobreescribir requests existentes con el mismo nombre (default: false)")
|
|
2243
|
+
},
|
|
2244
|
+
async (params) => {
|
|
2245
|
+
try {
|
|
2246
|
+
const raw = await readFile3(params.file, "utf-8");
|
|
2247
|
+
const collection = JSON.parse(raw);
|
|
2248
|
+
if (!collection.item || !Array.isArray(collection.item)) {
|
|
2249
|
+
return {
|
|
2250
|
+
content: [
|
|
2251
|
+
{
|
|
2252
|
+
type: "text",
|
|
2253
|
+
text: 'Error: El archivo no parece ser una Postman Collection v\xE1lida. Falta la propiedad "item".'
|
|
2254
|
+
}
|
|
2255
|
+
],
|
|
2256
|
+
isError: true
|
|
2257
|
+
};
|
|
2258
|
+
}
|
|
2259
|
+
const overwrite = params.overwrite ?? false;
|
|
2260
|
+
const extraTag = params.tag;
|
|
2261
|
+
const flatItems = flattenPostmanItems(collection.item, collection.auth);
|
|
2262
|
+
let imported = 0;
|
|
2263
|
+
let skipped = 0;
|
|
2264
|
+
const errors = [];
|
|
2265
|
+
for (const item of flatItems) {
|
|
2266
|
+
try {
|
|
2267
|
+
const saved = parsePostmanItem(item, extraTag);
|
|
2268
|
+
if (!saved) continue;
|
|
2269
|
+
if (!overwrite) {
|
|
2270
|
+
const existing = await storage.getCollection(saved.name);
|
|
2271
|
+
if (existing) {
|
|
2272
|
+
skipped++;
|
|
2273
|
+
continue;
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
await storage.saveCollection(saved);
|
|
2277
|
+
imported++;
|
|
2278
|
+
} catch (err) {
|
|
2279
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2280
|
+
errors.push(`${item.name ?? "unknown"}: ${msg}`);
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
const lines = [
|
|
2284
|
+
`Postman Collection importada: ${imported} requests guardados.`
|
|
2285
|
+
];
|
|
2286
|
+
if (skipped > 0) lines.push(`${skipped} requests omitidos (ya exist\xEDan, usa overwrite: true para sobreescribir).`);
|
|
2287
|
+
if (errors.length > 0) {
|
|
2288
|
+
lines.push(`${errors.length} errores:`);
|
|
2289
|
+
for (const e of errors) lines.push(` - ${e}`);
|
|
2290
|
+
}
|
|
2291
|
+
if (collection.info?.name) lines.push(`
|
|
2292
|
+
Colecci\xF3n: ${collection.info.name}`);
|
|
2293
|
+
return {
|
|
2294
|
+
content: [{ type: "text", text: lines.join("\n") }]
|
|
2295
|
+
};
|
|
2296
|
+
} catch (error) {
|
|
2297
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2298
|
+
return {
|
|
2299
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
2300
|
+
isError: true
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
);
|
|
2305
|
+
server.tool(
|
|
2306
|
+
"import_postman_environment",
|
|
2307
|
+
"Importa un Postman Environment (JSON) como entorno local. Soporta variables con valores initial/current.",
|
|
2308
|
+
{
|
|
2309
|
+
file: z8.string().describe("Ruta al archivo .postman_environment.json exportado de Postman"),
|
|
2310
|
+
name: z8.string().optional().describe("Nombre para el entorno (default: usa el nombre del archivo Postman)"),
|
|
2311
|
+
overwrite: z8.boolean().optional().describe("Sobreescribir si ya existe un entorno con el mismo nombre (default: false)"),
|
|
2312
|
+
activate: z8.boolean().optional().describe("Activar el entorno importado como entorno activo (default: false)")
|
|
2313
|
+
},
|
|
2314
|
+
async (params) => {
|
|
2315
|
+
try {
|
|
2316
|
+
const raw = await readFile3(params.file, "utf-8");
|
|
2317
|
+
const postmanEnv = JSON.parse(raw);
|
|
2318
|
+
if (!postmanEnv.values || !Array.isArray(postmanEnv.values)) {
|
|
2319
|
+
return {
|
|
2320
|
+
content: [
|
|
2321
|
+
{
|
|
2322
|
+
type: "text",
|
|
2323
|
+
text: 'Error: El archivo no parece ser un Postman Environment v\xE1lido. Falta la propiedad "values".'
|
|
2324
|
+
}
|
|
2325
|
+
],
|
|
2326
|
+
isError: true
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
const envName = params.name ?? postmanEnv.name ?? "postman-import";
|
|
2330
|
+
const overwrite = params.overwrite ?? false;
|
|
2331
|
+
const existing = await storage.getEnvironment(envName);
|
|
2332
|
+
if (existing && !overwrite) {
|
|
2333
|
+
return {
|
|
2334
|
+
content: [
|
|
2335
|
+
{
|
|
2336
|
+
type: "text",
|
|
2337
|
+
text: `Error: Ya existe un entorno '${envName}'. Usa overwrite: true para sobreescribir.`
|
|
2338
|
+
}
|
|
2339
|
+
],
|
|
2340
|
+
isError: true
|
|
2341
|
+
};
|
|
2342
|
+
}
|
|
2343
|
+
const variables = {};
|
|
2344
|
+
for (const v of postmanEnv.values) {
|
|
2345
|
+
if (!v.key) continue;
|
|
2346
|
+
if (v.enabled === false) continue;
|
|
2347
|
+
variables[v.key] = String(v.currentValue ?? v.value ?? "");
|
|
2348
|
+
}
|
|
2349
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2350
|
+
await storage.createEnvironment({
|
|
2351
|
+
name: envName,
|
|
2352
|
+
variables,
|
|
2353
|
+
createdAt: now,
|
|
2354
|
+
updatedAt: now
|
|
2355
|
+
});
|
|
2356
|
+
if (params.activate) {
|
|
2357
|
+
await storage.setActiveEnvironment(envName);
|
|
2358
|
+
}
|
|
2359
|
+
const lines = [
|
|
2360
|
+
`Postman Environment "${envName}" importado (${Object.keys(variables).length} variables).`
|
|
2361
|
+
];
|
|
2362
|
+
if (params.activate) lines.push("Entorno activado como activo.");
|
|
2363
|
+
return {
|
|
2364
|
+
content: [{ type: "text", text: lines.join("\n") }]
|
|
2365
|
+
};
|
|
2366
|
+
} catch (error) {
|
|
2367
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2368
|
+
return {
|
|
2369
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
2370
|
+
isError: true
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
);
|
|
2375
|
+
server.tool(
|
|
2376
|
+
"export_postman_environment",
|
|
2377
|
+
"Exporta un entorno como Postman Environment (JSON). Escribe el archivo en disco, importable directamente en Postman.",
|
|
2378
|
+
{
|
|
2379
|
+
name: z8.string().optional().describe("Nombre del entorno a exportar (default: entorno activo)"),
|
|
2380
|
+
output_dir: z8.string().optional().describe("Directorio donde guardar el archivo (default: ./postman/)")
|
|
2381
|
+
},
|
|
2382
|
+
async (params) => {
|
|
2383
|
+
try {
|
|
2384
|
+
let envName = params.name;
|
|
2385
|
+
if (!envName) {
|
|
2386
|
+
envName = await storage.getActiveEnvironment() ?? void 0;
|
|
2387
|
+
if (!envName) {
|
|
2388
|
+
return {
|
|
2389
|
+
content: [
|
|
2390
|
+
{
|
|
2391
|
+
type: "text",
|
|
2392
|
+
text: 'No hay entorno activo. Especifica un nombre con el par\xE1metro "name".'
|
|
2393
|
+
}
|
|
2394
|
+
],
|
|
2395
|
+
isError: true
|
|
2396
|
+
};
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
const env = await storage.getEnvironment(envName);
|
|
2400
|
+
if (!env) {
|
|
2401
|
+
return {
|
|
2402
|
+
content: [
|
|
2403
|
+
{
|
|
2404
|
+
type: "text",
|
|
2405
|
+
text: `Entorno '${envName}' no encontrado.`
|
|
2406
|
+
}
|
|
2407
|
+
],
|
|
2408
|
+
isError: true
|
|
2409
|
+
};
|
|
2410
|
+
}
|
|
2411
|
+
const postmanEnv = {
|
|
2412
|
+
name: env.name,
|
|
2413
|
+
values: Object.entries(env.variables).map(([key, value]) => ({
|
|
2414
|
+
key,
|
|
2415
|
+
value,
|
|
2416
|
+
type: "default",
|
|
2417
|
+
enabled: true
|
|
2418
|
+
})),
|
|
2419
|
+
_postman_variable_scope: "environment"
|
|
2420
|
+
};
|
|
2421
|
+
const json = JSON.stringify(postmanEnv, null, 2);
|
|
2422
|
+
const outputDir = params.output_dir ?? join2(process.cwd(), "postman");
|
|
2423
|
+
await mkdir2(outputDir, { recursive: true });
|
|
2424
|
+
const fileName = env.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") + ".postman_environment.json";
|
|
2425
|
+
const filePath = join2(outputDir, fileName);
|
|
2426
|
+
await writeFile2(filePath, json, "utf-8");
|
|
2427
|
+
return {
|
|
2428
|
+
content: [
|
|
2429
|
+
{
|
|
2430
|
+
type: "text",
|
|
2431
|
+
text: `Postman Environment "${env.name}" exportado (${Object.keys(env.variables).length} variables).
|
|
2432
|
+
|
|
2433
|
+
Archivo: ${filePath}
|
|
2434
|
+
|
|
2435
|
+
Importa este archivo en Postman: File \u2192 Import \u2192 selecciona el archivo.`
|
|
2436
|
+
}
|
|
2437
|
+
]
|
|
2438
|
+
};
|
|
2439
|
+
} catch (error) {
|
|
2440
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2441
|
+
return {
|
|
2442
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
2443
|
+
isError: true
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
);
|
|
2448
|
+
}
|
|
2449
|
+
function buildPostmanItem(saved, variables, resolveVars) {
|
|
2450
|
+
let config = saved.request;
|
|
2451
|
+
if (resolveVars) {
|
|
2452
|
+
const resolvedUrl = resolveUrl(config.url, variables);
|
|
2453
|
+
config = { ...config, url: resolvedUrl };
|
|
2454
|
+
config = interpolateRequest(config, variables);
|
|
2455
|
+
}
|
|
2456
|
+
const item = {
|
|
2457
|
+
name: saved.name,
|
|
2458
|
+
request: buildPostmanRequest(config)
|
|
2459
|
+
};
|
|
2460
|
+
return item;
|
|
2461
|
+
}
|
|
2462
|
+
function buildPostmanRequest(config) {
|
|
2463
|
+
const request = {
|
|
2464
|
+
method: config.method,
|
|
2465
|
+
header: buildPostmanHeaders(config.headers),
|
|
2466
|
+
url: buildPostmanUrl(config.url, config.query)
|
|
2467
|
+
};
|
|
2468
|
+
if (config.body !== void 0 && config.body !== null) {
|
|
2469
|
+
request.body = {
|
|
2470
|
+
mode: "raw",
|
|
2471
|
+
raw: typeof config.body === "string" ? config.body : JSON.stringify(config.body, null, 2),
|
|
2472
|
+
options: { raw: { language: "json" } }
|
|
2473
|
+
};
|
|
2474
|
+
const headers = request.header;
|
|
2475
|
+
if (!headers.some((h) => h.key.toLowerCase() === "content-type")) {
|
|
2476
|
+
headers.push({ key: "Content-Type", value: "application/json" });
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
if (config.auth) {
|
|
2480
|
+
request.auth = buildPostmanAuth(config.auth);
|
|
2481
|
+
}
|
|
2482
|
+
return request;
|
|
2483
|
+
}
|
|
2484
|
+
function buildPostmanHeaders(headers) {
|
|
2485
|
+
if (!headers) return [];
|
|
2486
|
+
return Object.entries(headers).map(([key, value]) => ({ key, value }));
|
|
2487
|
+
}
|
|
2488
|
+
function buildPostmanUrl(rawUrl, query) {
|
|
2489
|
+
const url = { raw: rawUrl };
|
|
2490
|
+
const match = rawUrl.match(/^(https?):\/\/([^/]+)(\/.*)?$/);
|
|
2491
|
+
if (match) {
|
|
2492
|
+
url.protocol = match[1];
|
|
2493
|
+
url.host = match[2].split(".");
|
|
2494
|
+
url.path = match[3] ? match[3].slice(1).split("/") : [];
|
|
2495
|
+
}
|
|
2496
|
+
if (query && Object.keys(query).length > 0) {
|
|
2497
|
+
url.query = Object.entries(query).map(([key, value]) => ({ key, value }));
|
|
2498
|
+
const queryStr = Object.entries(query).map(([k, v]) => `${k}=${v}`).join("&");
|
|
2499
|
+
url.raw = rawUrl + (rawUrl.includes("?") ? "&" : "?") + queryStr;
|
|
2500
|
+
}
|
|
2501
|
+
return url;
|
|
2502
|
+
}
|
|
2503
|
+
function buildPostmanAuth(auth) {
|
|
2504
|
+
switch (auth.type) {
|
|
2505
|
+
case "bearer":
|
|
2506
|
+
return {
|
|
2507
|
+
type: "bearer",
|
|
2508
|
+
bearer: [{ key: "token", value: auth.token ?? "", type: "string" }]
|
|
2509
|
+
};
|
|
2510
|
+
case "api-key":
|
|
2511
|
+
return {
|
|
2512
|
+
type: "apikey",
|
|
2513
|
+
apikey: [
|
|
2514
|
+
{ key: "key", value: auth.key ?? "", type: "string" },
|
|
2515
|
+
{ key: "value", value: auth.header ?? "X-API-Key", type: "string" },
|
|
2516
|
+
{ key: "in", value: "header", type: "string" }
|
|
2517
|
+
]
|
|
2518
|
+
};
|
|
2519
|
+
case "basic":
|
|
2520
|
+
return {
|
|
2521
|
+
type: "basic",
|
|
2522
|
+
basic: [
|
|
2523
|
+
{ key: "username", value: auth.username ?? "", type: "string" },
|
|
2524
|
+
{ key: "password", value: auth.password ?? "", type: "string" }
|
|
2525
|
+
]
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
function flattenPostmanItems(items, parentAuth, parentTags = []) {
|
|
2530
|
+
const result = [];
|
|
2531
|
+
for (const item of items) {
|
|
2532
|
+
if (item.item && Array.isArray(item.item)) {
|
|
2533
|
+
const folderTags = item.name ? [...parentTags, item.name] : parentTags;
|
|
2534
|
+
const folderAuth = item.auth ?? parentAuth;
|
|
2535
|
+
result.push(...flattenPostmanItems(item.item, folderAuth, folderTags));
|
|
2536
|
+
} else if (item.request) {
|
|
2537
|
+
result.push({
|
|
2538
|
+
...item,
|
|
2539
|
+
_folderTags: parentTags,
|
|
2540
|
+
_inheritedAuth: item.request?.auth ? void 0 : parentAuth
|
|
2541
|
+
});
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
return result;
|
|
2545
|
+
}
|
|
2546
|
+
var VALID_METHODS2 = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
2547
|
+
function parsePostmanItem(item, extraTag) {
|
|
2548
|
+
const req = item.request;
|
|
2549
|
+
if (!req) return null;
|
|
2550
|
+
const method = typeof req.method === "string" ? req.method.toUpperCase() : "GET";
|
|
2551
|
+
if (!VALID_METHODS2.has(method)) return null;
|
|
2552
|
+
const url = parsePostmanUrl(req.url);
|
|
2553
|
+
if (!url) return null;
|
|
2554
|
+
const headers = {};
|
|
2555
|
+
if (Array.isArray(req.header)) {
|
|
2556
|
+
for (const h of req.header) {
|
|
2557
|
+
if (h.disabled) continue;
|
|
2558
|
+
if (h.key && h.value !== void 0) {
|
|
2559
|
+
headers[h.key] = String(h.value);
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
const query = {};
|
|
2564
|
+
if (req.url && typeof req.url === "object" && Array.isArray(req.url.query)) {
|
|
2565
|
+
for (const q of req.url.query) {
|
|
2566
|
+
if (q.disabled) continue;
|
|
2567
|
+
if (q.key) {
|
|
2568
|
+
query[q.key] = String(q.value ?? "");
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
let body = void 0;
|
|
2573
|
+
if (req.body) {
|
|
2574
|
+
if (req.body.mode === "raw" && typeof req.body.raw === "string") {
|
|
2575
|
+
try {
|
|
2576
|
+
body = JSON.parse(req.body.raw);
|
|
2577
|
+
} catch {
|
|
2578
|
+
body = req.body.raw;
|
|
2579
|
+
}
|
|
2580
|
+
} else if (req.body.mode === "urlencoded" && Array.isArray(req.body.urlencoded)) {
|
|
2581
|
+
const formData = {};
|
|
2582
|
+
for (const p of req.body.urlencoded) {
|
|
2583
|
+
if (p.key) formData[p.key] = String(p.value ?? "");
|
|
2584
|
+
}
|
|
2585
|
+
body = formData;
|
|
2586
|
+
} else if (req.body.mode === "formdata" && Array.isArray(req.body.formdata)) {
|
|
2587
|
+
const formData = {};
|
|
2588
|
+
for (const p of req.body.formdata) {
|
|
2589
|
+
if (p.key && p.type !== "file") formData[p.key] = String(p.value ?? "");
|
|
2590
|
+
}
|
|
2591
|
+
body = formData;
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
const authSource = req.auth ?? item._inheritedAuth;
|
|
2595
|
+
const auth = parsePostmanAuth(authSource);
|
|
2596
|
+
const tags = [...item._folderTags ?? []];
|
|
2597
|
+
if (extraTag && !tags.includes(extraTag)) tags.push(extraTag);
|
|
2598
|
+
const name = item.name || `${method} ${url}`;
|
|
2599
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2600
|
+
const config = { method, url };
|
|
2601
|
+
if (Object.keys(headers).length > 0) config.headers = headers;
|
|
2602
|
+
if (Object.keys(query).length > 0) config.query = query;
|
|
2603
|
+
if (body !== void 0) config.body = body;
|
|
2604
|
+
if (auth) config.auth = auth;
|
|
2605
|
+
return {
|
|
2606
|
+
name,
|
|
2607
|
+
request: config,
|
|
2608
|
+
tags: tags.length > 0 ? tags : void 0,
|
|
2609
|
+
createdAt: now,
|
|
2610
|
+
updatedAt: now
|
|
2611
|
+
};
|
|
2612
|
+
}
|
|
2613
|
+
function parsePostmanUrl(url) {
|
|
2614
|
+
if (typeof url === "string") return url || null;
|
|
2615
|
+
if (url && typeof url === "object") {
|
|
2616
|
+
if (typeof url.raw === "string") return url.raw || null;
|
|
2617
|
+
const protocol = url.protocol ?? "https";
|
|
2618
|
+
const host = Array.isArray(url.host) ? url.host.join(".") : url.host;
|
|
2619
|
+
const path = Array.isArray(url.path) ? url.path.join("/") : url.path ?? "";
|
|
2620
|
+
if (host) return `${protocol}://${host}${path ? "/" + path : ""}`;
|
|
2621
|
+
}
|
|
2622
|
+
return null;
|
|
2623
|
+
}
|
|
2624
|
+
function parsePostmanAuth(auth) {
|
|
2625
|
+
if (!auth || !auth.type) return void 0;
|
|
2626
|
+
const getVal = (arr, key) => {
|
|
2627
|
+
if (!Array.isArray(arr)) return void 0;
|
|
2628
|
+
const item = arr.find((a) => a.key === key);
|
|
2629
|
+
return item?.value ? String(item.value) : void 0;
|
|
2630
|
+
};
|
|
2631
|
+
switch (auth.type) {
|
|
2632
|
+
case "bearer": {
|
|
2633
|
+
const token = getVal(auth.bearer, "token");
|
|
2634
|
+
return token ? { type: "bearer", token } : void 0;
|
|
2635
|
+
}
|
|
2636
|
+
case "apikey": {
|
|
2637
|
+
const key = getVal(auth.apikey, "key");
|
|
2638
|
+
const headerName = getVal(auth.apikey, "value");
|
|
2639
|
+
return key ? { type: "api-key", key, header: headerName } : void 0;
|
|
2640
|
+
}
|
|
2641
|
+
case "basic": {
|
|
2642
|
+
const username = getVal(auth.basic, "username");
|
|
2643
|
+
const password = getVal(auth.basic, "password");
|
|
2644
|
+
return username ? { type: "basic", username, password } : void 0;
|
|
2645
|
+
}
|
|
2646
|
+
default:
|
|
2647
|
+
return void 0;
|
|
2648
|
+
}
|
|
2142
2649
|
}
|
|
2143
2650
|
|
|
2144
2651
|
// src/tools/mock.ts
|
|
@@ -2450,7 +2957,7 @@ function registerLoadTestTool(server, storage) {
|
|
|
2450
2957
|
}
|
|
2451
2958
|
|
|
2452
2959
|
// src/server.ts
|
|
2453
|
-
var VERSION = "0.
|
|
2960
|
+
var VERSION = "0.10.0";
|
|
2454
2961
|
function createServer(storageDir) {
|
|
2455
2962
|
const server = new McpServer({
|
|
2456
2963
|
name: "api-testing-mcp",
|