@slashfi/agents-sdk 0.34.0 → 0.35.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 +2 -1
- package/dist/agent-definitions/auth.d.ts +3 -3
- package/dist/agent-definitions/auth.d.ts.map +1 -1
- package/dist/agent-definitions/auth.js +10 -4
- package/dist/agent-definitions/auth.js.map +1 -1
- package/dist/agent-definitions/config.d.ts.map +1 -1
- package/dist/agent-definitions/config.js.map +1 -1
- package/dist/agent-definitions/integrations.d.ts +12 -3
- package/dist/agent-definitions/integrations.d.ts.map +1 -1
- package/dist/agent-definitions/integrations.js +35 -16
- package/dist/agent-definitions/integrations.js.map +1 -1
- package/dist/agent-definitions/remote-registry.d.ts.map +1 -1
- package/dist/agent-definitions/remote-registry.js +17 -22
- package/dist/agent-definitions/remote-registry.js.map +1 -1
- package/dist/agent-definitions/users.d.ts.map +1 -1
- package/dist/agent-definitions/users.js.map +1 -1
- package/dist/auth-governance.js.map +1 -1
- package/dist/call-agent-schema.d.ts +128 -135
- package/dist/call-agent-schema.d.ts.map +1 -1
- package/dist/call-agent-schema.js +35 -5
- package/dist/call-agent-schema.js.map +1 -1
- package/dist/cjs/agent-definitions/auth.js +10 -4
- package/dist/cjs/agent-definitions/auth.js.map +1 -1
- package/dist/cjs/agent-definitions/config.js.map +1 -1
- package/dist/cjs/agent-definitions/integrations.js +35 -16
- package/dist/cjs/agent-definitions/integrations.js.map +1 -1
- package/dist/cjs/agent-definitions/remote-registry.js +17 -22
- package/dist/cjs/agent-definitions/remote-registry.js.map +1 -1
- package/dist/cjs/agent-definitions/users.js.map +1 -1
- package/dist/cjs/auth-governance.js.map +1 -1
- package/dist/cjs/call-agent-schema.js +35 -5
- package/dist/cjs/call-agent-schema.js.map +1 -1
- package/dist/cjs/define.js.map +1 -1
- package/dist/cjs/index.js +6 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/key-manager.js.map +1 -1
- package/dist/cjs/registry-consumer.js +60 -15
- package/dist/cjs/registry-consumer.js.map +1 -1
- package/dist/cjs/registry.js +3 -1
- package/dist/cjs/registry.js.map +1 -1
- package/dist/cjs/server.js +70 -121
- package/dist/cjs/server.js.map +1 -1
- package/dist/cjs/types.js +13 -0
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/validate.js +2 -2
- package/dist/cjs/validate.js.map +1 -1
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/key-manager.d.ts.map +1 -1
- package/dist/key-manager.js +1 -1
- package/dist/key-manager.js.map +1 -1
- package/dist/registry-consumer.d.ts +8 -8
- package/dist/registry-consumer.d.ts.map +1 -1
- package/dist/registry-consumer.js +60 -15
- package/dist/registry-consumer.js.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +3 -1
- package/dist/registry.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +63 -114
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +38 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -1
- package/dist/types.js.map +1 -1
- package/dist/validate.d.ts +15 -15
- package/dist/validate.js +2 -2
- package/dist/validate.js.map +1 -1
- package/package.json +1 -1
- package/src/agent-definitions/auth.ts +31 -14
- package/src/agent-definitions/config.ts +4 -4
- package/src/agent-definitions/integrations.ts +119 -63
- package/src/agent-definitions/remote-registry.ts +65 -38
- package/src/agent-definitions/users.ts +36 -3
- package/src/auth-governance.ts +2 -2
- package/src/call-agent-schema.test.ts +4 -1
- package/src/call-agent-schema.ts +39 -5
- package/src/consumer.test.ts +4 -1
- package/src/define.ts +18 -12
- package/src/index.ts +11 -0
- package/src/key-manager.ts +9 -2
- package/src/registry-consumer.ts +85 -24
- package/src/registry.ts +3 -1
- package/src/server.ts +122 -153
- package/src/types.ts +62 -0
- package/src/validate.ts +2 -2
|
@@ -28,7 +28,23 @@ import {
|
|
|
28
28
|
generateCollectionToken,
|
|
29
29
|
pendingCollections,
|
|
30
30
|
} from "../secret-collection.js";
|
|
31
|
-
import type {
|
|
31
|
+
import type {
|
|
32
|
+
AgentDefinition,
|
|
33
|
+
CallAgentRequest,
|
|
34
|
+
CallAgentResponse,
|
|
35
|
+
JsonSchema,
|
|
36
|
+
ToolContext,
|
|
37
|
+
ToolDefinition,
|
|
38
|
+
} from "../types.js";
|
|
39
|
+
|
|
40
|
+
/** Unwrap `execute_tool` payload from a registry response. */
|
|
41
|
+
function executeToolResult(
|
|
42
|
+
r: CallAgentResponse | undefined,
|
|
43
|
+
): unknown | undefined {
|
|
44
|
+
if (!r || r.success === false) return undefined;
|
|
45
|
+
if ("result" in r) return r.result;
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
32
48
|
|
|
33
49
|
// ============================================
|
|
34
50
|
// OAuth Config Types
|
|
@@ -73,6 +89,9 @@ export interface IntegrationOAuthConfig {
|
|
|
73
89
|
refreshGrantType?: string;
|
|
74
90
|
refreshBodyParams?: string[];
|
|
75
91
|
refreshHeaders?: Record<string, string>;
|
|
92
|
+
|
|
93
|
+
/** OAuth client id (plain string or secret: ref) */
|
|
94
|
+
clientId?: string;
|
|
76
95
|
}
|
|
77
96
|
|
|
78
97
|
// ============================================
|
|
@@ -106,7 +125,14 @@ export interface ProviderConfig {
|
|
|
106
125
|
* Agent path that handles this integration type.
|
|
107
126
|
* @integrations dispatches setup/connect/call to that agent's integrationMethods.
|
|
108
127
|
*/
|
|
109
|
-
agentPath
|
|
128
|
+
agentPath?: string;
|
|
129
|
+
/**
|
|
130
|
+
* @internal Secret store ids for credentials encrypted by @integrations.
|
|
131
|
+
*/
|
|
132
|
+
_clientIdSecretId?: string;
|
|
133
|
+
_clientSecretSecretId?: string;
|
|
134
|
+
/** Client id as plain string or secret: ref (when not using _clientIdSecretId) */
|
|
135
|
+
clientId?: string;
|
|
110
136
|
/**
|
|
111
137
|
* Scope of the integration:
|
|
112
138
|
* - 'user': per-user tokens (Slack, Notion, Linear)
|
|
@@ -515,7 +541,7 @@ export interface IntegrationsAgentOptions {
|
|
|
515
541
|
/** Registry instance for calling other agents' internal tools */
|
|
516
542
|
registry?: {
|
|
517
543
|
list?(): AgentDefinition[];
|
|
518
|
-
call(request:
|
|
544
|
+
call(request: CallAgentRequest): Promise<CallAgentResponse>;
|
|
519
545
|
};
|
|
520
546
|
|
|
521
547
|
/** Secret store for storing/resolving client credentials and tokens */
|
|
@@ -668,29 +694,32 @@ export function createIntegrationsAgent(
|
|
|
668
694
|
},
|
|
669
695
|
required: ["id", "name", "type", "api"],
|
|
670
696
|
},
|
|
671
|
-
execute: async (input:
|
|
697
|
+
execute: async (input: Record<string, unknown>, _ctx: ToolContext) => {
|
|
672
698
|
const config: ProviderConfig = {
|
|
673
|
-
id: input.id,
|
|
674
|
-
name: input.name,
|
|
675
|
-
agentPath: input.agentPath,
|
|
676
|
-
scope: input.scope,
|
|
677
|
-
docs: input.docs,
|
|
678
|
-
auth: input.auth,
|
|
679
|
-
api: input.api,
|
|
699
|
+
id: input.id as string,
|
|
700
|
+
name: input.name as string,
|
|
701
|
+
agentPath: input.agentPath as string | undefined,
|
|
702
|
+
scope: input.scope as "user" | "tenant" | undefined,
|
|
703
|
+
docs: input.docs as ProviderConfig["docs"],
|
|
704
|
+
auth: input.auth as ProviderConfig["auth"],
|
|
705
|
+
api: input.api as ProviderConfig["api"],
|
|
680
706
|
};
|
|
681
707
|
// Store client credentials encrypted and save secret IDs
|
|
682
708
|
const result: Record<string, unknown> = { success: true };
|
|
683
709
|
if (input.clientId) {
|
|
684
|
-
const secretId = await secretStore.store(
|
|
685
|
-
|
|
710
|
+
const secretId = await secretStore.store(
|
|
711
|
+
input.clientId as string,
|
|
712
|
+
SYSTEM_OWNER,
|
|
713
|
+
);
|
|
714
|
+
config._clientIdSecretId = secretId;
|
|
686
715
|
result.clientIdStored = true;
|
|
687
716
|
}
|
|
688
717
|
if (input.clientSecret) {
|
|
689
718
|
const secretId = await secretStore.store(
|
|
690
|
-
input.clientSecret,
|
|
719
|
+
input.clientSecret as string,
|
|
691
720
|
SYSTEM_OWNER,
|
|
692
721
|
);
|
|
693
|
-
|
|
722
|
+
config._clientSecretSecretId = secretId;
|
|
694
723
|
result.clientSecretStored = true;
|
|
695
724
|
}
|
|
696
725
|
|
|
@@ -703,10 +732,11 @@ export function createIntegrationsAgent(
|
|
|
703
732
|
action: "execute_tool",
|
|
704
733
|
path: config.agentPath,
|
|
705
734
|
tool: "setup_integration",
|
|
706
|
-
params: input.config ?? input,
|
|
735
|
+
params: (input.config ?? input) as Record<string, unknown>,
|
|
707
736
|
callerType: "system",
|
|
708
737
|
});
|
|
709
|
-
result.setupResult =
|
|
738
|
+
result.setupResult =
|
|
739
|
+
executeToolResult(setupResult) ?? setupResult;
|
|
710
740
|
} catch (err) {
|
|
711
741
|
result.setupError = err instanceof Error ? err.message : String(err);
|
|
712
742
|
}
|
|
@@ -857,7 +887,7 @@ export function createIntegrationsAgent(
|
|
|
857
887
|
const agents = options.registry.list?.() ?? [];
|
|
858
888
|
for (const agent of agents) {
|
|
859
889
|
const hasListTool = agent.tools?.some(
|
|
860
|
-
(t:
|
|
890
|
+
(t: ToolDefinition) => t.name === "list_integrations",
|
|
861
891
|
);
|
|
862
892
|
if (hasListTool && agent.config?.integration) {
|
|
863
893
|
const meta = {
|
|
@@ -878,9 +908,15 @@ export function createIntegrationsAgent(
|
|
|
878
908
|
callerType: "system",
|
|
879
909
|
})
|
|
880
910
|
: null;
|
|
881
|
-
const
|
|
882
|
-
callResult ??
|
|
883
|
-
|
|
911
|
+
const raw =
|
|
912
|
+
executeToolResult(callResult ?? undefined) ??
|
|
913
|
+
callResult ??
|
|
914
|
+
{ success: false };
|
|
915
|
+
const result = raw as {
|
|
916
|
+
success?: boolean;
|
|
917
|
+
data?: unknown;
|
|
918
|
+
};
|
|
919
|
+
if (result.success && result.data != null) {
|
|
884
920
|
// Flatten: if data has an array field, each item becomes an integration
|
|
885
921
|
const items = Array.isArray(result.data)
|
|
886
922
|
? result.data
|
|
@@ -982,7 +1018,7 @@ export function createIntegrationsAgent(
|
|
|
982
1018
|
params: { ...input, registryId: config.id },
|
|
983
1019
|
callerType: "system",
|
|
984
1020
|
});
|
|
985
|
-
return (connectResult
|
|
1021
|
+
return executeToolResult(connectResult) ?? connectResult;
|
|
986
1022
|
}
|
|
987
1023
|
|
|
988
1024
|
if (!config.auth)
|
|
@@ -998,26 +1034,26 @@ export function createIntegrationsAgent(
|
|
|
998
1034
|
// Check both _clientIdSecretId (from setup_integration direct) and
|
|
999
1035
|
// clientId field as secret:ref (from collect_secrets flow)
|
|
1000
1036
|
let clientId: string | null = null;
|
|
1001
|
-
const cidSecretId =
|
|
1037
|
+
const cidSecretId = config._clientIdSecretId;
|
|
1002
1038
|
if (cidSecretId && secretStore) {
|
|
1003
1039
|
clientId = await secretStore.resolve(cidSecretId, SYSTEM_OWNER);
|
|
1004
1040
|
}
|
|
1005
1041
|
// Also check if auth config has clientId as a secret:ref
|
|
1006
1042
|
if (
|
|
1007
1043
|
!clientId &&
|
|
1008
|
-
|
|
1009
|
-
typeof
|
|
1044
|
+
oauth.clientId &&
|
|
1045
|
+
typeof oauth.clientId === "string"
|
|
1010
1046
|
) {
|
|
1011
|
-
if (
|
|
1012
|
-
const refId =
|
|
1047
|
+
if (oauth.clientId.startsWith("secret:") && secretStore) {
|
|
1048
|
+
const refId = oauth.clientId.slice("secret:".length);
|
|
1013
1049
|
clientId = await secretStore.resolve(refId, SYSTEM_OWNER);
|
|
1014
|
-
} else if (!
|
|
1015
|
-
clientId =
|
|
1050
|
+
} else if (!oauth.clientId.startsWith("secret:")) {
|
|
1051
|
+
clientId = oauth.clientId;
|
|
1016
1052
|
}
|
|
1017
1053
|
}
|
|
1018
1054
|
// Check top-level config too
|
|
1019
|
-
if (!clientId &&
|
|
1020
|
-
const cid =
|
|
1055
|
+
if (!clientId && config.clientId) {
|
|
1056
|
+
const cid = config.clientId;
|
|
1021
1057
|
if (
|
|
1022
1058
|
typeof cid === "string" &&
|
|
1023
1059
|
cid.startsWith("secret:") &&
|
|
@@ -1112,14 +1148,17 @@ export function createIntegrationsAgent(
|
|
|
1112
1148
|
},
|
|
1113
1149
|
required: ["provider", "type"],
|
|
1114
1150
|
},
|
|
1115
|
-
execute: async (input:
|
|
1116
|
-
const config = await store.getProvider(input.provider);
|
|
1151
|
+
execute: async (input: Record<string, unknown>, ctx: ToolContext) => {
|
|
1152
|
+
const config = await store.getProvider(input.provider as string);
|
|
1117
1153
|
if (!config) return { error: `Provider '${input.provider}' not found` };
|
|
1118
1154
|
|
|
1119
1155
|
const userId = ctx.callerId;
|
|
1120
1156
|
|
|
1121
1157
|
// Get access token
|
|
1122
|
-
const connection = await store.getConnection(
|
|
1158
|
+
const connection = await store.getConnection(
|
|
1159
|
+
userId,
|
|
1160
|
+
input.provider as string,
|
|
1161
|
+
);
|
|
1123
1162
|
if (!connection) {
|
|
1124
1163
|
return {
|
|
1125
1164
|
error: `Not connected to '${input.provider}'. Use connect_integration first.`,
|
|
@@ -1135,8 +1174,8 @@ export function createIntegrationsAgent(
|
|
|
1135
1174
|
connection.refreshToken
|
|
1136
1175
|
) {
|
|
1137
1176
|
try {
|
|
1138
|
-
const rCidId =
|
|
1139
|
-
const rCsecId =
|
|
1177
|
+
const rCidId = config._clientIdSecretId;
|
|
1178
|
+
const rCsecId = config._clientSecretSecretId;
|
|
1140
1179
|
if (!rCidId || !rCsecId) {
|
|
1141
1180
|
throw new Error(
|
|
1142
1181
|
"No client credentials stored. Re-run setup_integration with clientId/clientSecret.",
|
|
@@ -1181,12 +1220,12 @@ export function createIntegrationsAgent(
|
|
|
1181
1220
|
return executeRestCall(
|
|
1182
1221
|
config,
|
|
1183
1222
|
{
|
|
1184
|
-
provider: input.provider,
|
|
1223
|
+
provider: input.provider as string,
|
|
1185
1224
|
type: "rest",
|
|
1186
|
-
method: input.method ?? "GET",
|
|
1187
|
-
path: input.path ?? "/",
|
|
1188
|
-
body: input.body,
|
|
1189
|
-
query: input.query,
|
|
1225
|
+
method: (input.method as RestCallInput["method"]) ?? "GET",
|
|
1226
|
+
path: (input.path as string) ?? "/",
|
|
1227
|
+
body: input.body as Record<string, unknown> | undefined,
|
|
1228
|
+
query: input.query as Record<string, string> | undefined,
|
|
1190
1229
|
},
|
|
1191
1230
|
accessToken,
|
|
1192
1231
|
);
|
|
@@ -1195,10 +1234,12 @@ export function createIntegrationsAgent(
|
|
|
1195
1234
|
return executeGraphqlCall(
|
|
1196
1235
|
config,
|
|
1197
1236
|
{
|
|
1198
|
-
provider: input.provider,
|
|
1237
|
+
provider: input.provider as string,
|
|
1199
1238
|
type: "graphql",
|
|
1200
|
-
query: input.graphqlQuery ??
|
|
1201
|
-
variables: input.variables
|
|
1239
|
+
query: String(input.graphqlQuery ?? ""),
|
|
1240
|
+
variables: input.variables as
|
|
1241
|
+
| Record<string, unknown>
|
|
1242
|
+
| undefined,
|
|
1202
1243
|
},
|
|
1203
1244
|
accessToken,
|
|
1204
1245
|
);
|
|
@@ -1249,7 +1290,7 @@ export function createIntegrationsAgent(
|
|
|
1249
1290
|
params: { ...input, registryId: config.id },
|
|
1250
1291
|
callerType: "system",
|
|
1251
1292
|
});
|
|
1252
|
-
return (connectResult
|
|
1293
|
+
return executeToolResult(connectResult) ?? connectResult;
|
|
1253
1294
|
}
|
|
1254
1295
|
|
|
1255
1296
|
if (!config.auth)
|
|
@@ -1266,8 +1307,8 @@ export function createIntegrationsAgent(
|
|
|
1266
1307
|
}
|
|
1267
1308
|
|
|
1268
1309
|
// Resolve client credentials from secret store via config
|
|
1269
|
-
const cbCidId =
|
|
1270
|
-
const cbCsecId =
|
|
1310
|
+
const cbCidId = config._clientIdSecretId;
|
|
1311
|
+
const cbCsecId = config._clientSecretSecretId;
|
|
1271
1312
|
if (!cbCidId || !cbCsecId) {
|
|
1272
1313
|
return { error: "No client credentials stored for this provider." };
|
|
1273
1314
|
}
|
|
@@ -1390,7 +1431,7 @@ export function createIntegrationsAgent(
|
|
|
1390
1431
|
// Fetch tool schema from registry
|
|
1391
1432
|
let toolSchema: {
|
|
1392
1433
|
name: string;
|
|
1393
|
-
inputSchema?:
|
|
1434
|
+
inputSchema?: JsonSchema;
|
|
1394
1435
|
description?: string;
|
|
1395
1436
|
} | null = null;
|
|
1396
1437
|
const registryUrl = input.registry;
|
|
@@ -1414,10 +1455,14 @@ export function createIntegrationsAgent(
|
|
|
1414
1455
|
},
|
|
1415
1456
|
}),
|
|
1416
1457
|
});
|
|
1417
|
-
const data = (await res.json()) as
|
|
1418
|
-
const
|
|
1458
|
+
const data = (await res.json()) as Record<string, unknown>;
|
|
1459
|
+
const resultBlock = data.result as
|
|
1460
|
+
| { content?: Array<{ text?: string }> }
|
|
1461
|
+
| undefined;
|
|
1462
|
+
const parsed = JSON.parse(resultBlock?.content?.[0]?.text ?? "{}");
|
|
1419
1463
|
const tools = parsed?.tools ?? parsed?.result?.tools ?? [];
|
|
1420
|
-
toolSchema =
|
|
1464
|
+
toolSchema =
|
|
1465
|
+
tools.find((t: { name: string }) => t.name === input.tool) ?? null;
|
|
1421
1466
|
|
|
1422
1467
|
if (!toolSchema?.inputSchema) {
|
|
1423
1468
|
return { error: `Tool '${input.tool}' not found on '${input.agent}'` };
|
|
@@ -1437,15 +1482,24 @@ export function createIntegrationsAgent(
|
|
|
1437
1482
|
required: boolean;
|
|
1438
1483
|
}> = [];
|
|
1439
1484
|
|
|
1440
|
-
for (const [name, def] of Object.entries(properties) as [
|
|
1485
|
+
for (const [name, def] of Object.entries(properties) as [
|
|
1486
|
+
string,
|
|
1487
|
+
Record<string, unknown>,
|
|
1488
|
+
][]) {
|
|
1441
1489
|
const isSecret = def.secret === true;
|
|
1442
1490
|
const isRequired = requiredFields.has(name);
|
|
1443
1491
|
const isProvided = name in providedParams;
|
|
1444
1492
|
if (isSecret || (isRequired && !isProvided)) {
|
|
1445
1493
|
fields.push({
|
|
1446
1494
|
name,
|
|
1447
|
-
type:
|
|
1448
|
-
|
|
1495
|
+
type:
|
|
1496
|
+
typeof def.type === "string"
|
|
1497
|
+
? def.type
|
|
1498
|
+
: Array.isArray(def.type)
|
|
1499
|
+
? def.type.join("|")
|
|
1500
|
+
: "string",
|
|
1501
|
+
description:
|
|
1502
|
+
typeof def.description === "string" ? def.description : name,
|
|
1449
1503
|
secret: isSecret,
|
|
1450
1504
|
required: isRequired,
|
|
1451
1505
|
});
|
|
@@ -1502,11 +1556,11 @@ export function createIntegrationsAgent(
|
|
|
1502
1556
|
inputSchema: { type: "object" as const, properties: {} },
|
|
1503
1557
|
execute: async () => {
|
|
1504
1558
|
const agents = options.registry?.list?.() ?? [];
|
|
1505
|
-
const results:
|
|
1559
|
+
const results: unknown[] = [];
|
|
1506
1560
|
if (options.registry) {
|
|
1507
1561
|
for (const agent of agents) {
|
|
1508
1562
|
const hasDiscoverTool = agent.tools?.some(
|
|
1509
|
-
(t:
|
|
1563
|
+
(t: ToolDefinition) => t.name === "discover_integrations",
|
|
1510
1564
|
);
|
|
1511
1565
|
if (hasDiscoverTool) {
|
|
1512
1566
|
try {
|
|
@@ -1518,8 +1572,9 @@ export function createIntegrationsAgent(
|
|
|
1518
1572
|
callerId: "@integrations",
|
|
1519
1573
|
callerType: "system",
|
|
1520
1574
|
});
|
|
1521
|
-
|
|
1522
|
-
|
|
1575
|
+
const payload = executeToolResult(res);
|
|
1576
|
+
if (payload && Array.isArray(payload)) {
|
|
1577
|
+
results.push(...payload);
|
|
1523
1578
|
}
|
|
1524
1579
|
} catch {}
|
|
1525
1580
|
}
|
|
@@ -1542,14 +1597,14 @@ export function createIntegrationsAgent(
|
|
|
1542
1597
|
},
|
|
1543
1598
|
execute: async (input: { agent_path?: string }) => {
|
|
1544
1599
|
const agents = options.registry?.list?.() ?? [];
|
|
1545
|
-
const results:
|
|
1600
|
+
const results: unknown[] = [];
|
|
1546
1601
|
if (options.registry) {
|
|
1547
1602
|
const targetAgents = input.agent_path
|
|
1548
|
-
? agents.filter((a:
|
|
1603
|
+
? agents.filter((a: AgentDefinition) => a.path === input.agent_path)
|
|
1549
1604
|
: agents;
|
|
1550
1605
|
for (const agent of targetAgents) {
|
|
1551
1606
|
const hasListTool = agent.tools?.some(
|
|
1552
|
-
(t:
|
|
1607
|
+
(t: ToolDefinition) => t.name === "list_integrations",
|
|
1553
1608
|
);
|
|
1554
1609
|
if (hasListTool) {
|
|
1555
1610
|
try {
|
|
@@ -1561,8 +1616,9 @@ export function createIntegrationsAgent(
|
|
|
1561
1616
|
callerId: "@integrations",
|
|
1562
1617
|
callerType: "system",
|
|
1563
1618
|
});
|
|
1564
|
-
|
|
1565
|
-
|
|
1619
|
+
const payload = executeToolResult(res);
|
|
1620
|
+
if (payload && Array.isArray(payload)) {
|
|
1621
|
+
results.push(...payload);
|
|
1566
1622
|
}
|
|
1567
1623
|
} catch {}
|
|
1568
1624
|
}
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
import { defineAgent, defineTool } from "../define.js";
|
|
28
28
|
import type {
|
|
29
29
|
AgentDefinition,
|
|
30
|
-
IntegrationMethodContext,
|
|
31
30
|
IntegrationMethodResult,
|
|
32
31
|
ToolContext,
|
|
32
|
+
ToolDefinition,
|
|
33
33
|
} from "../types.js";
|
|
34
34
|
import type { SecretStore } from "./secrets.js";
|
|
35
35
|
|
|
@@ -53,6 +53,39 @@ interface RegistryConnection {
|
|
|
53
53
|
|
|
54
54
|
const ENTITY_TYPE = "remote-registry-connections";
|
|
55
55
|
|
|
56
|
+
/** JSON-RPC response for MCP `tools/call` over HTTP. */
|
|
57
|
+
interface McpToolsCallRpcBody {
|
|
58
|
+
result?: {
|
|
59
|
+
content?: Array<{ type: string; text?: string }>;
|
|
60
|
+
};
|
|
61
|
+
error?: { message: string };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface ProxyCallToolInput {
|
|
65
|
+
registryId: string;
|
|
66
|
+
action: string;
|
|
67
|
+
path: string;
|
|
68
|
+
tool: string;
|
|
69
|
+
params?: Record<string, unknown>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface AddConnectionToolInput {
|
|
73
|
+
id: string;
|
|
74
|
+
name?: string;
|
|
75
|
+
url: string;
|
|
76
|
+
remoteTenantId?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Typical fields from POST /oauth/token JSON body. */
|
|
80
|
+
interface OAuthTokenJsonBody {
|
|
81
|
+
access_token?: string;
|
|
82
|
+
tenant_id?: string;
|
|
83
|
+
user_id?: string;
|
|
84
|
+
error?: string;
|
|
85
|
+
error_description?: string;
|
|
86
|
+
authorize_url?: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
56
89
|
export function createRemoteRegistryAgent(
|
|
57
90
|
options: RemoteRegistryAgentOptions,
|
|
58
91
|
): AgentDefinition {
|
|
@@ -122,7 +155,7 @@ export function createRemoteRegistryAgent(
|
|
|
122
155
|
url: string,
|
|
123
156
|
jwt: string,
|
|
124
157
|
request: Record<string, unknown>,
|
|
125
|
-
): Promise<
|
|
158
|
+
): Promise<unknown> {
|
|
126
159
|
const res = await globalThis.fetch(url, {
|
|
127
160
|
method: "POST",
|
|
128
161
|
headers: {
|
|
@@ -136,7 +169,7 @@ export function createRemoteRegistryAgent(
|
|
|
136
169
|
params: { name: "call_agent", arguments: { request } },
|
|
137
170
|
}),
|
|
138
171
|
});
|
|
139
|
-
const rpc = (await res.json()) as
|
|
172
|
+
const rpc = (await res.json()) as McpToolsCallRpcBody;
|
|
140
173
|
const text = rpc.result?.content?.[0]?.text;
|
|
141
174
|
if (!text) return rpc.result;
|
|
142
175
|
const parsed = JSON.parse(text);
|
|
@@ -167,7 +200,7 @@ export function createRemoteRegistryAgent(
|
|
|
167
200
|
tool: string;
|
|
168
201
|
params?: Record<string, unknown>;
|
|
169
202
|
},
|
|
170
|
-
): Promise<
|
|
203
|
+
): Promise<unknown> {
|
|
171
204
|
const conn = await loadConnection(ownerId, registryId);
|
|
172
205
|
if (!conn) {
|
|
173
206
|
throw new Error(
|
|
@@ -202,7 +235,7 @@ export function createRemoteRegistryAgent(
|
|
|
202
235
|
},
|
|
203
236
|
required: ["registryId", "action", "path", "tool"],
|
|
204
237
|
},
|
|
205
|
-
execute: async (input:
|
|
238
|
+
execute: async (input: ProxyCallToolInput, _ctx: ToolContext) => {
|
|
206
239
|
return proxyCall("system", input.registryId, {
|
|
207
240
|
action: input.action,
|
|
208
241
|
path: input.path,
|
|
@@ -230,7 +263,7 @@ export function createRemoteRegistryAgent(
|
|
|
230
263
|
},
|
|
231
264
|
required: ["id", "url"],
|
|
232
265
|
},
|
|
233
|
-
execute: async (input:
|
|
266
|
+
execute: async (input: AddConnectionToolInput, _ctx: ToolContext) => {
|
|
234
267
|
const conn: RegistryConnection = {
|
|
235
268
|
id: input.id,
|
|
236
269
|
name: input.name ?? input.id,
|
|
@@ -248,7 +281,7 @@ export function createRemoteRegistryAgent(
|
|
|
248
281
|
description: "List all connected remote registries.",
|
|
249
282
|
visibility: "public" as const,
|
|
250
283
|
inputSchema: { type: "object" as const, properties: {} },
|
|
251
|
-
execute: async (_input:
|
|
284
|
+
execute: async (_input: Record<string, unknown>, _ctx: ToolContext) => {
|
|
252
285
|
const all = await loadAllConnections("system");
|
|
253
286
|
return {
|
|
254
287
|
connections: Object.values(all).map((c) => ({
|
|
@@ -264,7 +297,7 @@ export function createRemoteRegistryAgent(
|
|
|
264
297
|
// Extract setup/connect as standalone functions to avoid circular reference
|
|
265
298
|
const setupFn = async (
|
|
266
299
|
params: Record<string, unknown>,
|
|
267
|
-
_ctx:
|
|
300
|
+
_ctx: ToolContext,
|
|
268
301
|
): Promise<IntegrationMethodResult> => {
|
|
269
302
|
console.log(
|
|
270
303
|
"[remote-registry] setupFn called with:",
|
|
@@ -294,7 +327,7 @@ export function createRemoteRegistryAgent(
|
|
|
294
327
|
redirect_uri: params.redirect_uri ?? "",
|
|
295
328
|
}),
|
|
296
329
|
});
|
|
297
|
-
const tokenData = (await tokenRes.json()) as
|
|
330
|
+
const tokenData = (await tokenRes.json()) as OAuthTokenJsonBody;
|
|
298
331
|
if (!tokenData.access_token && !tokenData.tenant_id) {
|
|
299
332
|
return {
|
|
300
333
|
success: false,
|
|
@@ -319,23 +352,16 @@ export function createRemoteRegistryAgent(
|
|
|
319
352
|
};
|
|
320
353
|
}
|
|
321
354
|
|
|
322
|
-
// Phase 1:
|
|
323
|
-
const
|
|
324
|
-
console.log("[setupFn] fetching
|
|
325
|
-
const
|
|
326
|
-
console.log("[setupFn]
|
|
327
|
-
if (!
|
|
355
|
+
// Phase 1: Verify JWKS at origin, establish trust, then request OIDC
|
|
356
|
+
const jwksUri = `${new URL(baseUrl).origin}/.well-known/jwks.json`;
|
|
357
|
+
console.log("[setupFn] fetching JWKS:", jwksUri);
|
|
358
|
+
const jwksRes = await globalThis.fetch(jwksUri);
|
|
359
|
+
console.log("[setupFn] JWKS status:", jwksRes.status);
|
|
360
|
+
if (!jwksRes.ok)
|
|
328
361
|
return {
|
|
329
362
|
success: false,
|
|
330
|
-
error: `
|
|
363
|
+
error: `JWKS not reachable at ${jwksUri}`,
|
|
331
364
|
};
|
|
332
|
-
const remoteConfig = (await configRes.json()) as any;
|
|
333
|
-
if (remoteConfig.jwks_uri) {
|
|
334
|
-
console.log("[setupFn] fetching JWKS:", remoteConfig.jwks_uri);
|
|
335
|
-
const jwksRes = await globalThis.fetch(remoteConfig.jwks_uri);
|
|
336
|
-
console.log("[setupFn] JWKS status:", jwksRes.status);
|
|
337
|
-
if (!jwksRes.ok) return { success: false, error: "JWKS not reachable" };
|
|
338
|
-
}
|
|
339
365
|
if (addTrustedIssuer) {
|
|
340
366
|
console.log("[setupFn] adding trusted issuer:", baseUrl);
|
|
341
367
|
await addTrustedIssuer(baseUrl);
|
|
@@ -361,7 +387,7 @@ export function createRemoteRegistryAgent(
|
|
|
361
387
|
}),
|
|
362
388
|
});
|
|
363
389
|
console.log("[setupFn] token status:", tokenRes.status);
|
|
364
|
-
const tokenData = (await tokenRes.json()) as
|
|
390
|
+
const tokenData = (await tokenRes.json()) as OAuthTokenJsonBody;
|
|
365
391
|
console.log(
|
|
366
392
|
"[setupFn] tokenData:",
|
|
367
393
|
JSON.stringify(tokenData).substring(0, 300),
|
|
@@ -414,7 +440,7 @@ export function createRemoteRegistryAgent(
|
|
|
414
440
|
|
|
415
441
|
const connectFn = async (
|
|
416
442
|
params: Record<string, unknown>,
|
|
417
|
-
ctx:
|
|
443
|
+
ctx: ToolContext,
|
|
418
444
|
): Promise<IntegrationMethodResult> => {
|
|
419
445
|
const registryId = params.registryId as string;
|
|
420
446
|
const redirectUri = (params.redirectUri as string) ?? "";
|
|
@@ -443,7 +469,7 @@ export function createRemoteRegistryAgent(
|
|
|
443
469
|
redirect_uri: redirectUri,
|
|
444
470
|
}),
|
|
445
471
|
});
|
|
446
|
-
const tokenData = (await tokenRes.json()) as
|
|
472
|
+
const tokenData = (await tokenRes.json()) as OAuthTokenJsonBody;
|
|
447
473
|
if (tokenData.access_token)
|
|
448
474
|
return {
|
|
449
475
|
success: true,
|
|
@@ -498,27 +524,28 @@ export function createRemoteRegistryAgent(
|
|
|
498
524
|
category: "infrastructure",
|
|
499
525
|
description:
|
|
500
526
|
"Connect to a remote agent registry via JWKS trust exchange.",
|
|
501
|
-
setup: (params, ctx) => setupFn(params, ctx
|
|
502
|
-
connect: (params, ctx) => connectFn(params, ctx
|
|
527
|
+
setup: (params, ctx) => setupFn(params, ctx),
|
|
528
|
+
connect: (params, ctx) => connectFn(params, ctx),
|
|
503
529
|
async discover(params) {
|
|
504
530
|
const url = (params.url as string) ?? "";
|
|
505
531
|
try {
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
532
|
+
const base = url.replace(/\/$/, "");
|
|
533
|
+
const origin = new URL(base).origin;
|
|
534
|
+
const jwksUri = `${origin}/.well-known/jwks.json`;
|
|
535
|
+
const res = await globalThis.fetch(jwksUri);
|
|
536
|
+
if (!res.ok) {
|
|
510
537
|
return {
|
|
511
538
|
success: false,
|
|
512
|
-
error: `
|
|
539
|
+
error: `JWKS not reachable at ${jwksUri}`,
|
|
513
540
|
};
|
|
514
|
-
|
|
541
|
+
}
|
|
515
542
|
return {
|
|
516
543
|
success: true,
|
|
517
544
|
data: {
|
|
518
545
|
url,
|
|
519
|
-
issuer:
|
|
520
|
-
grantTypes:
|
|
521
|
-
jwksUri
|
|
546
|
+
issuer: origin,
|
|
547
|
+
grantTypes: ["client_credentials", "jwt_exchange"],
|
|
548
|
+
jwksUri,
|
|
522
549
|
},
|
|
523
550
|
};
|
|
524
551
|
} catch (err) {
|
|
@@ -558,6 +585,6 @@ export function createRemoteRegistryAgent(
|
|
|
558
585
|
return { success: true, data: conn };
|
|
559
586
|
},
|
|
560
587
|
},
|
|
561
|
-
tools: [proxyTool, listTool, addConnectionTool] as
|
|
588
|
+
tools: [proxyTool, listTool, addConnectionTool] as ToolDefinition<ToolContext>[],
|
|
562
589
|
});
|
|
563
590
|
}
|
|
@@ -230,6 +230,39 @@ export interface UsersAgentOptions {
|
|
|
230
230
|
store: UserStore;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
interface CreateUserToolInput {
|
|
234
|
+
id?: string;
|
|
235
|
+
tenantId: string;
|
|
236
|
+
email?: string;
|
|
237
|
+
name?: string;
|
|
238
|
+
avatarUrl?: string;
|
|
239
|
+
metadata?: Record<string, unknown>;
|
|
240
|
+
externalRef?: { issuer: string; userId: string };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
interface UpdateUserToolInput {
|
|
244
|
+
userId: string;
|
|
245
|
+
email?: string;
|
|
246
|
+
name?: string;
|
|
247
|
+
avatarUrl?: string;
|
|
248
|
+
metadata?: Record<string, unknown>;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
interface LinkIdentityToolInput {
|
|
252
|
+
userId: string;
|
|
253
|
+
provider: string;
|
|
254
|
+
providerUserId: string;
|
|
255
|
+
email?: string;
|
|
256
|
+
name?: string;
|
|
257
|
+
avatarUrl?: string;
|
|
258
|
+
accessToken?: string;
|
|
259
|
+
refreshToken?: string;
|
|
260
|
+
expiresAt?: number;
|
|
261
|
+
tokenType?: string;
|
|
262
|
+
scopes?: string[];
|
|
263
|
+
metadata?: Record<string, unknown>;
|
|
264
|
+
}
|
|
265
|
+
|
|
233
266
|
export function createUsersAgent(options: UsersAgentOptions): AgentDefinition {
|
|
234
267
|
const { store } = options;
|
|
235
268
|
|
|
@@ -268,7 +301,7 @@ export function createUsersAgent(options: UsersAgentOptions): AgentDefinition {
|
|
|
268
301
|
},
|
|
269
302
|
required: ["tenantId"],
|
|
270
303
|
},
|
|
271
|
-
execute: async (input:
|
|
304
|
+
execute: async (input: CreateUserToolInput, __ctx: ToolContext) => {
|
|
272
305
|
// If externalRef provided, check if identity already exists
|
|
273
306
|
if (input.externalRef) {
|
|
274
307
|
const existing = await store.findIdentityByProviderUserId(
|
|
@@ -365,7 +398,7 @@ export function createUsersAgent(options: UsersAgentOptions): AgentDefinition {
|
|
|
365
398
|
},
|
|
366
399
|
required: ["userId"],
|
|
367
400
|
},
|
|
368
|
-
execute: async (input:
|
|
401
|
+
execute: async (input: UpdateUserToolInput, __ctx: ToolContext) => {
|
|
369
402
|
const { userId, ...updates } = input;
|
|
370
403
|
const user = await store.updateUser(userId, updates);
|
|
371
404
|
if (!user) return { error: `User '${userId}' not found` };
|
|
@@ -416,7 +449,7 @@ export function createUsersAgent(options: UsersAgentOptions): AgentDefinition {
|
|
|
416
449
|
},
|
|
417
450
|
required: ["userId", "provider", "providerUserId"],
|
|
418
451
|
},
|
|
419
|
-
execute: async (input:
|
|
452
|
+
execute: async (input: LinkIdentityToolInput, __ctx: ToolContext) => {
|
|
420
453
|
// Check if identity already exists
|
|
421
454
|
const existing = await store.getIdentityByProvider(
|
|
422
455
|
input.userId,
|