@slashfi/agents-sdk 0.77.3 → 0.78.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/dist/call-agent-schema.d.ts +12 -12
- package/dist/cjs/config-store.js +265 -68
- package/dist/cjs/config-store.js.map +1 -1
- package/dist/cjs/define-config.js.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/config-store.d.ts +34 -4
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +265 -68
- package/dist/config-store.js.map +1 -1
- package/dist/define-config.d.ts +28 -4
- package/dist/define-config.d.ts.map +1 -1
- package/dist/define-config.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/config-store.test.ts +345 -28
- package/src/config-store.ts +735 -250
- package/src/define-config.ts +47 -21
- package/src/index.ts +16 -13
package/dist/cjs/config-store.js
CHANGED
|
@@ -52,12 +52,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
52
52
|
})();
|
|
53
53
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
54
|
exports.createAdk = createAdk;
|
|
55
|
-
const define_config_js_1 = require("./define-config.js");
|
|
56
|
-
const registry_consumer_js_1 = require("./registry-consumer.js");
|
|
57
|
-
const crypto_js_1 = require("./crypto.js");
|
|
58
55
|
const adk_error_js_1 = require("./adk-error.js");
|
|
56
|
+
const crypto_js_1 = require("./crypto.js");
|
|
57
|
+
const define_config_js_1 = require("./define-config.js");
|
|
59
58
|
const mcp_client_js_1 = require("./mcp-client.js");
|
|
59
|
+
const registry_consumer_js_1 = require("./registry-consumer.js");
|
|
60
60
|
const CONFIG_PATH = "consumer-config.json";
|
|
61
|
+
const REGISTRY_CACHE_PATH = "registry-cache.json";
|
|
61
62
|
const SECRET_PREFIX = "secret:";
|
|
62
63
|
// ============================================
|
|
63
64
|
// Internal helpers
|
|
@@ -110,7 +111,9 @@ async function decryptConfigSecrets(obj, encryptionKey) {
|
|
|
110
111
|
if (typeof value === "string" && value.startsWith(SECRET_PREFIX)) {
|
|
111
112
|
result[key] = await (0, crypto_js_1.decryptSecret)(value.slice(SECRET_PREFIX.length), encryptionKey);
|
|
112
113
|
}
|
|
113
|
-
else if (value !== null &&
|
|
114
|
+
else if (value !== null &&
|
|
115
|
+
typeof value === "object" &&
|
|
116
|
+
!Array.isArray(value)) {
|
|
114
117
|
result[key] = await decryptConfigSecrets(value, encryptionKey);
|
|
115
118
|
}
|
|
116
119
|
else {
|
|
@@ -128,7 +131,7 @@ async function decryptConfigSecrets(obj, encryptionKey) {
|
|
|
128
131
|
* Fallback: _httpStatus from tool result body
|
|
129
132
|
*/
|
|
130
133
|
function isUnauthorized(result) {
|
|
131
|
-
if (!result || typeof result !==
|
|
134
|
+
if (!result || typeof result !== "object")
|
|
132
135
|
return false;
|
|
133
136
|
const r = result;
|
|
134
137
|
// Primary: HTTP status forwarded by the registry and set by callRegistry
|
|
@@ -143,17 +146,21 @@ function isUnauthorized(result) {
|
|
|
143
146
|
// ============================================
|
|
144
147
|
// Local auth form HTML
|
|
145
148
|
// ============================================
|
|
146
|
-
const esc = (s) => s
|
|
149
|
+
const esc = (s) => s
|
|
150
|
+
.replace(/&/g, "&")
|
|
151
|
+
.replace(/</g, "<")
|
|
152
|
+
.replace(/>/g, ">")
|
|
153
|
+
.replace(/"/g, """);
|
|
147
154
|
function renderCredentialForm(name, fields, error) {
|
|
148
|
-
const fieldHtml = fields
|
|
155
|
+
const fieldHtml = fields
|
|
156
|
+
.map((f) => `
|
|
149
157
|
<div class="field">
|
|
150
158
|
<label for="${esc(f.name)}">${esc(f.label)}</label>
|
|
151
159
|
${f.description ? `<p class="desc">${esc(f.description)}</p>` : ""}
|
|
152
160
|
<input id="${esc(f.name)}" name="${esc(f.name)}" type="${f.secret ? "password" : "text"}" required autocomplete="off" spellcheck="false" />
|
|
153
|
-
</div>`)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
: "";
|
|
161
|
+
</div>`)
|
|
162
|
+
.join("");
|
|
163
|
+
const errorHtml = error ? `<div class="error">${esc(error)}</div>` : "";
|
|
157
164
|
return `<!DOCTYPE html>
|
|
158
165
|
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
159
166
|
<title>Authenticate \u2014 ${esc(name)}</title>
|
|
@@ -216,6 +223,95 @@ function createAdk(fs, options = {}) {
|
|
|
216
223
|
async function writeConfig(config) {
|
|
217
224
|
await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
218
225
|
}
|
|
226
|
+
// -------------------------------------------------------------------------
|
|
227
|
+
// Registry cache helpers
|
|
228
|
+
//
|
|
229
|
+
// The cache is purely an internal optimization for the adk's read paths
|
|
230
|
+
// (`ref.list()`, `ref.get()`). Writes happen as side-effects of methods
|
|
231
|
+
// that already call the registry (`ref.add()`, `ref.inspect()`); the
|
|
232
|
+
// public surface never grows new methods. Cache failures (missing file,
|
|
233
|
+
// malformed JSON, fs errors during write) are swallowed so the registry
|
|
234
|
+
// cache can never break a registry operation.
|
|
235
|
+
// -------------------------------------------------------------------------
|
|
236
|
+
async function readRegistryCache() {
|
|
237
|
+
try {
|
|
238
|
+
const content = await fs.readFile(REGISTRY_CACHE_PATH);
|
|
239
|
+
if (!content)
|
|
240
|
+
return { refs: {} };
|
|
241
|
+
const parsed = JSON.parse(content);
|
|
242
|
+
return { refs: parsed.refs ?? {} };
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
return { refs: {} };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async function writeRegistryCache(cache) {
|
|
249
|
+
try {
|
|
250
|
+
await fs.writeFile(REGISTRY_CACHE_PATH, JSON.stringify(cache, null, 2));
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
// Best-effort. A failed cache write should never break the operation
|
|
254
|
+
// that triggered it.
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Project an inspect/list response into the slim shape we cache. Drops
|
|
259
|
+
* `inputSchema` (too large) and `fullTokens` (registry-internal). Returns
|
|
260
|
+
* undefined if the response carries nothing worth caching.
|
|
261
|
+
*/
|
|
262
|
+
function buildCacheEntry(ref, info) {
|
|
263
|
+
if (!info)
|
|
264
|
+
return undefined;
|
|
265
|
+
const toolSource = info.tools ?? info.toolSummaries;
|
|
266
|
+
const tools = toolSource?.map((t) => {
|
|
267
|
+
const slim = { name: t.name };
|
|
268
|
+
if (t.description !== undefined)
|
|
269
|
+
slim.description = t.description;
|
|
270
|
+
return slim;
|
|
271
|
+
});
|
|
272
|
+
if (info.description === undefined && (!tools || tools.length === 0)) {
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
275
|
+
const entry = {
|
|
276
|
+
ref,
|
|
277
|
+
fetchedAt: new Date().toISOString(),
|
|
278
|
+
};
|
|
279
|
+
if (info.description !== undefined)
|
|
280
|
+
entry.description = info.description;
|
|
281
|
+
if (tools && tools.length > 0)
|
|
282
|
+
entry.tools = tools;
|
|
283
|
+
return entry;
|
|
284
|
+
}
|
|
285
|
+
async function upsertRegistryCacheEntry(name, entry) {
|
|
286
|
+
if (!entry)
|
|
287
|
+
return;
|
|
288
|
+
const cache = await readRegistryCache();
|
|
289
|
+
cache.refs[name] = entry;
|
|
290
|
+
await writeRegistryCache(cache);
|
|
291
|
+
}
|
|
292
|
+
async function removeRegistryCacheEntry(name) {
|
|
293
|
+
const cache = await readRegistryCache();
|
|
294
|
+
if (!(name in cache.refs))
|
|
295
|
+
return;
|
|
296
|
+
delete cache.refs[name];
|
|
297
|
+
await writeRegistryCache(cache);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Hydrate a `ResolvedRef` with cached registry metadata when available.
|
|
301
|
+
* Pure: never mutates input. Leaves `description` / `tools` undefined when
|
|
302
|
+
* the cache has no entry, so callers can apply their own UX fallback.
|
|
303
|
+
*/
|
|
304
|
+
function hydrateFromCache(ref, cache) {
|
|
305
|
+
const cached = cache.refs[ref.name];
|
|
306
|
+
if (!cached)
|
|
307
|
+
return ref;
|
|
308
|
+
const next = { ...ref };
|
|
309
|
+
if (cached.description !== undefined)
|
|
310
|
+
next.description = cached.description;
|
|
311
|
+
if (cached.tools !== undefined)
|
|
312
|
+
next.tools = cached.tools;
|
|
313
|
+
return next;
|
|
314
|
+
}
|
|
219
315
|
/**
|
|
220
316
|
* Store a secret value in a ref's config, encrypted if encryptionKey is set.
|
|
221
317
|
* The value is stored inline as "secret:<encrypted>" in consumer-config.json.
|
|
@@ -326,7 +422,10 @@ function createAdk(fs, options = {}) {
|
|
|
326
422
|
let reqId = 0;
|
|
327
423
|
let sessionId;
|
|
328
424
|
async function rpc(method, rpcParams) {
|
|
329
|
-
const reqHeaders = {
|
|
425
|
+
const reqHeaders = {
|
|
426
|
+
...headers,
|
|
427
|
+
...(sessionId ? { "Mcp-Session-Id": sessionId } : {}),
|
|
428
|
+
};
|
|
330
429
|
const res = await globalThis.fetch(url, {
|
|
331
430
|
method: "POST",
|
|
332
431
|
headers: reqHeaders,
|
|
@@ -367,7 +466,7 @@ function createAdk(fs, options = {}) {
|
|
|
367
466
|
}
|
|
368
467
|
return undefined;
|
|
369
468
|
}
|
|
370
|
-
const json = await res.json();
|
|
469
|
+
const json = (await res.json());
|
|
371
470
|
if (json.error)
|
|
372
471
|
throw new Error(`MCP RPC error: ${json.error.message}`);
|
|
373
472
|
return json.result;
|
|
@@ -379,20 +478,32 @@ function createAdk(fs, options = {}) {
|
|
|
379
478
|
clientInfo: { name: "adk", version: "1.0.0" },
|
|
380
479
|
});
|
|
381
480
|
await rpc("notifications/initialized").catch(() => { });
|
|
382
|
-
const result = await rpc("tools/call", {
|
|
481
|
+
const result = (await rpc("tools/call", {
|
|
482
|
+
name: toolName,
|
|
483
|
+
arguments: params,
|
|
484
|
+
}));
|
|
383
485
|
const textContent = result?.content?.find((c) => c.type === "text");
|
|
384
486
|
if (textContent?.text) {
|
|
385
487
|
try {
|
|
386
|
-
return {
|
|
488
|
+
return {
|
|
489
|
+
success: true,
|
|
490
|
+
result: JSON.parse(textContent.text),
|
|
491
|
+
};
|
|
387
492
|
}
|
|
388
493
|
catch {
|
|
389
|
-
return {
|
|
494
|
+
return {
|
|
495
|
+
success: true,
|
|
496
|
+
result: textContent.text,
|
|
497
|
+
};
|
|
390
498
|
}
|
|
391
499
|
}
|
|
392
500
|
return { success: true, result };
|
|
393
501
|
}
|
|
394
502
|
catch (err) {
|
|
395
|
-
return {
|
|
503
|
+
return {
|
|
504
|
+
success: false,
|
|
505
|
+
error: err instanceof Error ? err.message : String(err),
|
|
506
|
+
};
|
|
396
507
|
}
|
|
397
508
|
}
|
|
398
509
|
function callbackUrl() {
|
|
@@ -405,7 +516,7 @@ function createAdk(fs, options = {}) {
|
|
|
405
516
|
const res = await globalThis.fetch(url);
|
|
406
517
|
if (!res.ok)
|
|
407
518
|
return null;
|
|
408
|
-
const data = await res.json();
|
|
519
|
+
const data = (await res.json());
|
|
409
520
|
if (data.authorization_endpoint && data.token_endpoint) {
|
|
410
521
|
return data;
|
|
411
522
|
}
|
|
@@ -775,7 +886,9 @@ function createAdk(fs, options = {}) {
|
|
|
775
886
|
...final,
|
|
776
887
|
proxy: {
|
|
777
888
|
mode: discovered.proxy.mode,
|
|
778
|
-
...(discovered.proxy.agent && {
|
|
889
|
+
...(discovered.proxy.agent && {
|
|
890
|
+
agent: discovered.proxy.agent,
|
|
891
|
+
}),
|
|
779
892
|
},
|
|
780
893
|
};
|
|
781
894
|
}
|
|
@@ -902,7 +1015,12 @@ function createAdk(fs, options = {}) {
|
|
|
902
1015
|
}));
|
|
903
1016
|
return results.map((r) => r.status === "fulfilled"
|
|
904
1017
|
? r.value
|
|
905
|
-
: {
|
|
1018
|
+
: {
|
|
1019
|
+
name: "unknown",
|
|
1020
|
+
url: "unknown",
|
|
1021
|
+
status: "error",
|
|
1022
|
+
error: "unknown",
|
|
1023
|
+
});
|
|
906
1024
|
},
|
|
907
1025
|
async auth(nameOrUrl, credential) {
|
|
908
1026
|
// Encrypt the secret value up-front so the write path is uniform;
|
|
@@ -1182,7 +1300,10 @@ function createAdk(fs, options = {}) {
|
|
|
1182
1300
|
entry = { ...entry, scheme: "registry" };
|
|
1183
1301
|
}
|
|
1184
1302
|
else if (entry.url) {
|
|
1185
|
-
entry = {
|
|
1303
|
+
entry = {
|
|
1304
|
+
...entry,
|
|
1305
|
+
scheme: entry.url.startsWith("http") ? "https" : "mcp",
|
|
1306
|
+
};
|
|
1186
1307
|
}
|
|
1187
1308
|
else {
|
|
1188
1309
|
throw new adk_error_js_1.AdkError({
|
|
@@ -1210,6 +1331,7 @@ function createAdk(fs, options = {}) {
|
|
|
1210
1331
|
details: { ref: entry.ref, scheme: entry.scheme },
|
|
1211
1332
|
});
|
|
1212
1333
|
}
|
|
1334
|
+
let cacheEntry;
|
|
1213
1335
|
if (hasRegistries || entry.sourceRegistry?.url) {
|
|
1214
1336
|
try {
|
|
1215
1337
|
const consumer = await buildConsumerForRef(entry);
|
|
@@ -1217,9 +1339,10 @@ function createAdk(fs, options = {}) {
|
|
|
1217
1339
|
const info = await consumer.inspect(agentToInspect);
|
|
1218
1340
|
const requiresValidation = !!entry.sourceRegistry;
|
|
1219
1341
|
if (requiresValidation) {
|
|
1220
|
-
const hasContent = info &&
|
|
1221
|
-
(info.
|
|
1222
|
-
|
|
1342
|
+
const hasContent = info &&
|
|
1343
|
+
(info.description ||
|
|
1344
|
+
(info.tools && info.tools.length > 0) ||
|
|
1345
|
+
(info.toolSummaries && info.toolSummaries.length > 0));
|
|
1223
1346
|
if (!hasContent) {
|
|
1224
1347
|
// Inspect returned empty — fall back to browse to check if agent exists
|
|
1225
1348
|
const registryUrl = entry.sourceRegistry?.url;
|
|
@@ -1241,7 +1364,11 @@ function createAdk(fs, options = {}) {
|
|
|
1241
1364
|
code: "REF_NOT_FOUND",
|
|
1242
1365
|
message: `Agent "${entry.ref}" not found on ${registryHint}`,
|
|
1243
1366
|
hint: "Check available agents with: adk registry browse",
|
|
1244
|
-
details: {
|
|
1367
|
+
details: {
|
|
1368
|
+
ref: entry.ref,
|
|
1369
|
+
sourceRegistry: entry.sourceRegistry,
|
|
1370
|
+
scheme: entry.scheme,
|
|
1371
|
+
},
|
|
1245
1372
|
});
|
|
1246
1373
|
}
|
|
1247
1374
|
}
|
|
@@ -1251,10 +1378,11 @@ function createAdk(fs, options = {}) {
|
|
|
1251
1378
|
const agentMode = info?.mode;
|
|
1252
1379
|
if (agentMode)
|
|
1253
1380
|
entry.mode = agentMode;
|
|
1254
|
-
if (info?.upstream && !entry.url && agentMode !==
|
|
1381
|
+
if (info?.upstream && !entry.url && agentMode !== "api") {
|
|
1255
1382
|
entry.url = info.upstream;
|
|
1256
1383
|
entry.scheme = entry.scheme ?? "mcp";
|
|
1257
1384
|
}
|
|
1385
|
+
cacheEntry = buildCacheEntry(entry.ref, info);
|
|
1258
1386
|
}
|
|
1259
1387
|
catch (err) {
|
|
1260
1388
|
if (err instanceof adk_error_js_1.AdkError)
|
|
@@ -1263,13 +1391,17 @@ function createAdk(fs, options = {}) {
|
|
|
1263
1391
|
code: "REGISTRY_UNREACHABLE",
|
|
1264
1392
|
message: `Could not reach registry to validate "${entry.ref}"`,
|
|
1265
1393
|
hint: "Check your registry connection with: adk registry test",
|
|
1266
|
-
details: {
|
|
1394
|
+
details: {
|
|
1395
|
+
ref: entry.ref,
|
|
1396
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1397
|
+
},
|
|
1267
1398
|
cause: err,
|
|
1268
1399
|
});
|
|
1269
1400
|
}
|
|
1270
1401
|
}
|
|
1271
1402
|
const refs = [...(config.refs ?? []), entry];
|
|
1272
1403
|
await writeConfig({ ...config, refs });
|
|
1404
|
+
await upsertRegistryCacheEntry(name, cacheEntry);
|
|
1273
1405
|
return { security };
|
|
1274
1406
|
},
|
|
1275
1407
|
async remove(name) {
|
|
@@ -1281,15 +1413,27 @@ function createAdk(fs, options = {}) {
|
|
|
1281
1413
|
if (refs.length === before)
|
|
1282
1414
|
return false;
|
|
1283
1415
|
await writeConfig({ ...config, refs });
|
|
1416
|
+
await removeRegistryCacheEntry(name);
|
|
1284
1417
|
return true;
|
|
1285
1418
|
},
|
|
1286
1419
|
async list() {
|
|
1287
|
-
const config = await
|
|
1288
|
-
|
|
1420
|
+
const [config, cache] = await Promise.all([
|
|
1421
|
+
readConfig(),
|
|
1422
|
+
readRegistryCache(),
|
|
1423
|
+
]);
|
|
1424
|
+
return (config.refs ?? [])
|
|
1425
|
+
.map(define_config_js_1.normalizeRef)
|
|
1426
|
+
.map((r) => hydrateFromCache(r, cache));
|
|
1289
1427
|
},
|
|
1290
1428
|
async get(name) {
|
|
1291
|
-
const config = await
|
|
1292
|
-
|
|
1429
|
+
const [config, cache] = await Promise.all([
|
|
1430
|
+
readConfig(),
|
|
1431
|
+
readRegistryCache(),
|
|
1432
|
+
]);
|
|
1433
|
+
const found = findRef(config.refs ?? [], name);
|
|
1434
|
+
if (!found)
|
|
1435
|
+
return null;
|
|
1436
|
+
return hydrateFromCache(found, cache);
|
|
1293
1437
|
},
|
|
1294
1438
|
async update(name, updates) {
|
|
1295
1439
|
const config = await readConfig();
|
|
@@ -1335,27 +1479,35 @@ function createAdk(fs, options = {}) {
|
|
|
1335
1479
|
if (!entry)
|
|
1336
1480
|
throw new Error(`Ref "${name}" not found`);
|
|
1337
1481
|
const consumer = await buildConsumerForRef(entry);
|
|
1338
|
-
|
|
1482
|
+
const result = await consumer.inspect(entry.sourceRegistry?.agentPath ?? entry.ref, entry.sourceRegistry?.url, opts);
|
|
1483
|
+
// Side-effect: refresh the registry cache so subsequent ref.list()
|
|
1484
|
+
// / ref.get() calls see the latest description and tool summaries
|
|
1485
|
+
// without another network round-trip. Strips inputSchema (caller's
|
|
1486
|
+
// `result` is unaffected — it still carries the full data).
|
|
1487
|
+
await upsertRegistryCacheEntry(name, buildCacheEntry(entry.ref, result));
|
|
1488
|
+
return result;
|
|
1339
1489
|
},
|
|
1340
1490
|
async call(name, tool, params) {
|
|
1341
1491
|
const config = await readConfig();
|
|
1342
1492
|
const entry = findRef(config.refs ?? [], name);
|
|
1343
1493
|
if (!entry)
|
|
1344
1494
|
throw new Error(`Ref "${name}" not found`);
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1495
|
+
const accessToken = (await readRefSecret(name, "access_token")) ??
|
|
1496
|
+
(await readRefSecret(name, "api_key")) ??
|
|
1497
|
+
(await readRefSecret(name, "token"));
|
|
1348
1498
|
// Resolve custom headers from config (e.g. { "X-API-Key": "secret:..." })
|
|
1349
1499
|
const refConfig = (entry.config ?? {});
|
|
1350
1500
|
const rawHeaders = refConfig.headers;
|
|
1351
1501
|
let resolvedHeaders;
|
|
1352
|
-
if (rawHeaders && typeof rawHeaders ===
|
|
1502
|
+
if (rawHeaders && typeof rawHeaders === "object") {
|
|
1353
1503
|
resolvedHeaders = {};
|
|
1354
1504
|
for (const [k, v] of Object.entries(rawHeaders)) {
|
|
1355
|
-
if (typeof v ===
|
|
1505
|
+
if (typeof v === "string" &&
|
|
1506
|
+
v.startsWith(SECRET_PREFIX) &&
|
|
1507
|
+
options.encryptionKey) {
|
|
1356
1508
|
resolvedHeaders[k] = await (0, crypto_js_1.decryptSecret)(v.slice(SECRET_PREFIX.length), options.encryptionKey);
|
|
1357
1509
|
}
|
|
1358
|
-
else if (typeof v ===
|
|
1510
|
+
else if (typeof v === "string") {
|
|
1359
1511
|
resolvedHeaders[k] = v;
|
|
1360
1512
|
}
|
|
1361
1513
|
}
|
|
@@ -1363,8 +1515,8 @@ function createAdk(fs, options = {}) {
|
|
|
1363
1515
|
const doCall = async (token) => {
|
|
1364
1516
|
// Direct MCP only for redirect/proxy agents with an MCP upstream.
|
|
1365
1517
|
// API-mode agents must go through the registry (it does REST translation).
|
|
1366
|
-
const agentMode = entry.mode ??
|
|
1367
|
-
if (token && entry.url && agentMode !==
|
|
1518
|
+
const agentMode = entry.mode ?? "redirect";
|
|
1519
|
+
if (token && entry.url && agentMode !== "api") {
|
|
1368
1520
|
return callMcpDirect(entry.url, tool, params ?? {}, token, resolvedHeaders);
|
|
1369
1521
|
}
|
|
1370
1522
|
const consumer = await buildConsumerForRef(entry);
|
|
@@ -1480,10 +1632,15 @@ function createAdk(fs, options = {}) {
|
|
|
1480
1632
|
}
|
|
1481
1633
|
else if (security.type === "apiKey") {
|
|
1482
1634
|
const apiKeySec = security;
|
|
1483
|
-
const toStorageKey = (headerName) => headerName
|
|
1635
|
+
const toStorageKey = (headerName) => headerName
|
|
1636
|
+
.toLowerCase()
|
|
1637
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
1638
|
+
.replace(/^_|_$/g, "");
|
|
1484
1639
|
// config.headers: { "Header-Name": "value" } — check by header name (case-insensitive)
|
|
1485
1640
|
const configHeaders = entry?.config?.headers;
|
|
1486
|
-
const configHeaderKeys = configHeaders
|
|
1641
|
+
const configHeaderKeys = configHeaders
|
|
1642
|
+
? Object.keys(configHeaders)
|
|
1643
|
+
: [];
|
|
1487
1644
|
const hasConfigHeader = (name) => configHeaderKeys.some((k) => k.toLowerCase() === name.toLowerCase());
|
|
1488
1645
|
// Collect all declared header names from the security scheme
|
|
1489
1646
|
const declaredHeaders = apiKeySec.headers
|
|
@@ -1532,7 +1689,9 @@ function createAdk(fs, options = {}) {
|
|
|
1532
1689
|
// agent. The registry owns the client_id/secret and returns an authorize
|
|
1533
1690
|
// URL pointing at the registry's OAuth callback domain, so the user
|
|
1534
1691
|
// completes the flow against the registry instead of localhost.
|
|
1535
|
-
const proxy = await resolveProxyForRef(entry, {
|
|
1692
|
+
const proxy = await resolveProxyForRef(entry, {
|
|
1693
|
+
preferLocal: opts?.preferLocal,
|
|
1694
|
+
});
|
|
1536
1695
|
if (proxy) {
|
|
1537
1696
|
const params = { name };
|
|
1538
1697
|
if (opts?.apiKey !== undefined)
|
|
@@ -1553,12 +1712,18 @@ function createAdk(fs, options = {}) {
|
|
|
1553
1712
|
}
|
|
1554
1713
|
if (security.type === "apiKey") {
|
|
1555
1714
|
const apiKeySec = security;
|
|
1556
|
-
const toStorageKey = (headerName) => headerName
|
|
1715
|
+
const toStorageKey = (headerName) => headerName
|
|
1716
|
+
.toLowerCase()
|
|
1717
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
1718
|
+
.replace(/^_|_$/g, "");
|
|
1557
1719
|
// Check existing config.headers
|
|
1558
1720
|
const existingHeaders = (entry.config ?? {}).headers;
|
|
1559
1721
|
// Collect declared headers: from security.headers or security.name
|
|
1560
1722
|
const declaredHeaders = apiKeySec.headers
|
|
1561
|
-
? Object.entries(apiKeySec.headers).map(([h, meta]) => ({
|
|
1723
|
+
? Object.entries(apiKeySec.headers).map(([h, meta]) => ({
|
|
1724
|
+
headerName: h,
|
|
1725
|
+
description: meta.description,
|
|
1726
|
+
}))
|
|
1562
1727
|
: apiKeySec.name
|
|
1563
1728
|
? [{ headerName: apiKeySec.name }]
|
|
1564
1729
|
: [];
|
|
@@ -1568,12 +1733,13 @@ function createAdk(fs, options = {}) {
|
|
|
1568
1733
|
for (const { headerName, description } of declaredHeaders) {
|
|
1569
1734
|
const storageKey = toStorageKey(headerName);
|
|
1570
1735
|
// Check: credentials param → existing config.headers → legacy config key → resolve callback
|
|
1571
|
-
const value = opts?.credentials?.[storageKey]
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
??
|
|
1576
|
-
|
|
1736
|
+
const value = opts?.credentials?.[storageKey] ??
|
|
1737
|
+
opts?.credentials?.[headerName] ??
|
|
1738
|
+
(existingHeaders &&
|
|
1739
|
+
Object.entries(existingHeaders).find(([k]) => k.toLowerCase() === headerName.toLowerCase())?.[1]) ??
|
|
1740
|
+
opts?.apiKey ??
|
|
1741
|
+
(await readRefSecret(name, storageKey)) ??
|
|
1742
|
+
(await tryResolve(storageKey));
|
|
1577
1743
|
if (value) {
|
|
1578
1744
|
resolvedHeaders[headerName] = value;
|
|
1579
1745
|
}
|
|
@@ -1593,22 +1759,28 @@ function createAdk(fs, options = {}) {
|
|
|
1593
1759
|
const encKey = options.encryptionKey;
|
|
1594
1760
|
const headersToStore = {};
|
|
1595
1761
|
for (const [h, v] of Object.entries(resolvedHeaders)) {
|
|
1596
|
-
headersToStore[h] = encKey
|
|
1762
|
+
headersToStore[h] = encKey
|
|
1763
|
+
? `${SECRET_PREFIX}${await (0, crypto_js_1.encryptSecret)(v, encKey)}`
|
|
1764
|
+
: v;
|
|
1597
1765
|
}
|
|
1598
1766
|
await ref.update(name, { config: { headers: headersToStore } });
|
|
1599
1767
|
return { type: "apiKey", complete: true };
|
|
1600
1768
|
}
|
|
1601
1769
|
// Fallback: no headers declared → generic api_key
|
|
1602
|
-
const key = opts?.credentials?.["api_key"] ??
|
|
1770
|
+
const key = opts?.credentials?.["api_key"] ??
|
|
1771
|
+
opts?.apiKey ??
|
|
1772
|
+
(await tryResolve("api_key"));
|
|
1603
1773
|
if (!key) {
|
|
1604
1774
|
return {
|
|
1605
1775
|
type: "apiKey",
|
|
1606
1776
|
complete: false,
|
|
1607
|
-
fields: [
|
|
1777
|
+
fields: [
|
|
1778
|
+
{
|
|
1608
1779
|
name: "api_key",
|
|
1609
1780
|
label: "API Key",
|
|
1610
1781
|
secret: true,
|
|
1611
|
-
}
|
|
1782
|
+
},
|
|
1783
|
+
],
|
|
1612
1784
|
};
|
|
1613
1785
|
}
|
|
1614
1786
|
await storeRefSecret(name, "api_key", key);
|
|
@@ -1618,14 +1790,22 @@ function createAdk(fs, options = {}) {
|
|
|
1618
1790
|
const httpSec = security;
|
|
1619
1791
|
const isBasic = httpSec.scheme === "basic";
|
|
1620
1792
|
if (isBasic) {
|
|
1621
|
-
const username = opts?.credentials?.["username"] ?? await tryResolve("username");
|
|
1622
|
-
const password = opts?.credentials?.["password"] ?? await tryResolve("password");
|
|
1793
|
+
const username = opts?.credentials?.["username"] ?? (await tryResolve("username"));
|
|
1794
|
+
const password = opts?.credentials?.["password"] ?? (await tryResolve("password"));
|
|
1623
1795
|
if (!username || !password) {
|
|
1624
1796
|
const missingFields = [];
|
|
1625
1797
|
if (!username)
|
|
1626
|
-
missingFields.push({
|
|
1798
|
+
missingFields.push({
|
|
1799
|
+
name: "username",
|
|
1800
|
+
label: "Username",
|
|
1801
|
+
secret: false,
|
|
1802
|
+
});
|
|
1627
1803
|
if (!password)
|
|
1628
|
-
missingFields.push({
|
|
1804
|
+
missingFields.push({
|
|
1805
|
+
name: "password",
|
|
1806
|
+
label: "Password",
|
|
1807
|
+
secret: true,
|
|
1808
|
+
});
|
|
1629
1809
|
return { type: "http", complete: false, fields: missingFields };
|
|
1630
1810
|
}
|
|
1631
1811
|
// Store as base64 encoded basic auth token
|
|
@@ -1634,7 +1814,9 @@ function createAdk(fs, options = {}) {
|
|
|
1634
1814
|
return { type: "http", complete: true };
|
|
1635
1815
|
}
|
|
1636
1816
|
// Bearer token
|
|
1637
|
-
const token = opts?.credentials?.["token"] ??
|
|
1817
|
+
const token = opts?.credentials?.["token"] ??
|
|
1818
|
+
opts?.apiKey ??
|
|
1819
|
+
(await tryResolve("token"));
|
|
1638
1820
|
if (!token) {
|
|
1639
1821
|
return {
|
|
1640
1822
|
type: "http",
|
|
@@ -1691,8 +1873,9 @@ function createAdk(fs, options = {}) {
|
|
|
1691
1873
|
const supportedAuthMethods = metadata.token_endpoint_auth_methods_supported ?? ["none"];
|
|
1692
1874
|
const preferredMethod = supportedAuthMethods.includes("none")
|
|
1693
1875
|
? "none"
|
|
1694
|
-
: supportedAuthMethods[0] ?? "client_secret_post";
|
|
1695
|
-
const securityClientName = security
|
|
1876
|
+
: (supportedAuthMethods[0] ?? "client_secret_post");
|
|
1877
|
+
const securityClientName = security
|
|
1878
|
+
.clientName;
|
|
1696
1879
|
const reg = await (0, mcp_client_js_1.dynamicClientRegistration)(metadata.registration_endpoint, {
|
|
1697
1880
|
clientName: securityClientName ?? options.oauthClientName ?? "adk",
|
|
1698
1881
|
redirectUris: [redirectUri],
|
|
@@ -1710,10 +1893,18 @@ function createAdk(fs, options = {}) {
|
|
|
1710
1893
|
// Return fields telling the caller what OAuth credentials to provide
|
|
1711
1894
|
const missingFields = [];
|
|
1712
1895
|
if (!clientId) {
|
|
1713
|
-
missingFields.push({
|
|
1896
|
+
missingFields.push({
|
|
1897
|
+
name: "client_id",
|
|
1898
|
+
label: "Client ID",
|
|
1899
|
+
secret: false,
|
|
1900
|
+
});
|
|
1714
1901
|
}
|
|
1715
1902
|
// Always ask for client_secret alongside client_id — most providers need it
|
|
1716
|
-
missingFields.push({
|
|
1903
|
+
missingFields.push({
|
|
1904
|
+
name: "client_secret",
|
|
1905
|
+
label: "Client Secret",
|
|
1906
|
+
secret: true,
|
|
1907
|
+
});
|
|
1717
1908
|
return { type: "oauth2", complete: false, fields: missingFields };
|
|
1718
1909
|
}
|
|
1719
1910
|
// State ties the callback back to this ref. Encode as base64 JSON
|
|
@@ -1735,7 +1926,9 @@ function createAdk(fs, options = {}) {
|
|
|
1735
1926
|
const scopes = agentScopes.length > 0
|
|
1736
1927
|
? [
|
|
1737
1928
|
...agentScopes,
|
|
1738
|
-
...(metadata.scopes_supported?.includes(
|
|
1929
|
+
...(metadata.scopes_supported?.includes("openid")
|
|
1930
|
+
? ["openid"]
|
|
1931
|
+
: []),
|
|
1739
1932
|
]
|
|
1740
1933
|
: metadata.scopes_supported;
|
|
1741
1934
|
// Read provider-specific authorization params from the agent's security section
|
|
@@ -1784,7 +1977,9 @@ function createAdk(fs, options = {}) {
|
|
|
1784
1977
|
// owns the credential store, so the user needs to submit via
|
|
1785
1978
|
// whatever UI the registry exposes. Supporting this through the
|
|
1786
1979
|
// proxy would need a remote form endpoint — out of scope here.
|
|
1787
|
-
if (result.fields &&
|
|
1980
|
+
if (result.fields &&
|
|
1981
|
+
result.fields.length > 0 &&
|
|
1982
|
+
result.type !== "oauth2") {
|
|
1788
1983
|
if (proxy) {
|
|
1789
1984
|
throw new Error(`Ref "${name}" is sourced from a proxied registry; submit credentials through ${proxy.agent} instead of a local form.`);
|
|
1790
1985
|
}
|
|
@@ -1944,7 +2139,7 @@ function createAdk(fs, options = {}) {
|
|
|
1944
2139
|
});
|
|
1945
2140
|
if (!res.ok)
|
|
1946
2141
|
return null;
|
|
1947
|
-
const data = await res.json();
|
|
2142
|
+
const data = (await res.json());
|
|
1948
2143
|
const newAccessToken = data.access_token;
|
|
1949
2144
|
if (!newAccessToken)
|
|
1950
2145
|
return null;
|
|
@@ -1979,7 +2174,9 @@ function createAdk(fs, options = {}) {
|
|
|
1979
2174
|
try {
|
|
1980
2175
|
stateContext = JSON.parse(atob(params.state));
|
|
1981
2176
|
}
|
|
1982
|
-
catch {
|
|
2177
|
+
catch {
|
|
2178
|
+
/* state wasn't base64 JSON — legacy format */
|
|
2179
|
+
}
|
|
1983
2180
|
return { refName: pending.refName, complete: true, stateContext };
|
|
1984
2181
|
}
|
|
1985
2182
|
return { registry, ref, readConfig, writeConfig, handleCallback };
|