@slashfi/agents-sdk 0.11.1 → 0.12.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.
Files changed (41) hide show
  1. package/dist/agent-definitions/auth.d.ts +4 -1
  2. package/dist/agent-definitions/auth.d.ts.map +1 -1
  3. package/dist/agent-definitions/auth.js +48 -3
  4. package/dist/agent-definitions/auth.js.map +1 -1
  5. package/dist/agent-definitions/integrations.d.ts +2 -14
  6. package/dist/agent-definitions/integrations.d.ts.map +1 -1
  7. package/dist/agent-definitions/integrations.js +46 -17
  8. package/dist/agent-definitions/integrations.js.map +1 -1
  9. package/dist/agent-definitions/remote-registry.d.ts +19 -14
  10. package/dist/agent-definitions/remote-registry.d.ts.map +1 -1
  11. package/dist/agent-definitions/remote-registry.js +207 -381
  12. package/dist/agent-definitions/remote-registry.js.map +1 -1
  13. package/dist/agent-definitions/users.d.ts.map +1 -1
  14. package/dist/agent-definitions/users.js +29 -1
  15. package/dist/agent-definitions/users.js.map +1 -1
  16. package/dist/define.d.ts +6 -4
  17. package/dist/define.d.ts.map +1 -1
  18. package/dist/define.js +82 -3
  19. package/dist/define.js.map +1 -1
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/jwt.js +1 -1
  24. package/dist/jwt.js.map +1 -1
  25. package/dist/server.d.ts +42 -5
  26. package/dist/server.d.ts.map +1 -1
  27. package/dist/server.js +223 -62
  28. package/dist/server.js.map +1 -1
  29. package/dist/types.d.ts +53 -1
  30. package/dist/types.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/agent-definitions/auth.ts +57 -5
  33. package/src/agent-definitions/integrations.ts +51 -26
  34. package/src/agent-definitions/remote-registry.ts +210 -513
  35. package/src/agent-definitions/users.ts +35 -1
  36. package/src/define.ts +98 -6
  37. package/src/index.ts +2 -1
  38. package/src/jwt.ts +1 -1
  39. package/src/server.test.ts +284 -0
  40. package/src/server.ts +331 -75
  41. package/src/types.ts +44 -1
@@ -1,133 +1,51 @@
1
1
  /**
2
- * Remote Registry Agent
2
+ * Remote Registry Agent (JWKS Auth)
3
3
  *
4
4
  * Integration agent for connecting to remote agent registries.
5
- * Uses the IntegrationMethods pattern so @integrations can discover
6
- * and interact with it uniformly via setup/connect/list/get/update.
5
+ * Uses JWKS trust exchange + jwt_exchange for authentication.
6
+ * No client credentials stored uses signJwt for outbound calls.
7
7
  *
8
- * Each remote registry connection stores:
9
- * - url: the registry's base URL
10
- * - tenantId: the tenant created on the remote registry
11
- * - clientId + clientSecret: credentials for authentication
8
+ * Flow:
9
+ * setup: discover JWKS add trusted issuer → call @auth/create_tenant → store connection
10
+ * connect: POST /oauth/token jwt_exchange identity_required /oauth/authorize linked
11
+ * proxy: sign JWT POST to remote MCP endpoint
12
12
  *
13
13
  * @example
14
14
  * ```typescript
15
- * import { createRemoteRegistryAgent, createAgentRegistry } from '@slashfi/agents-sdk';
15
+ * import { createRemoteRegistryAgent, createAgentServer } from '@slashfi/agents-sdk';
16
16
  *
17
- * const registry = createAgentRegistry();
18
- * registry.register(createRemoteRegistryAgent({ secretStore }));
17
+ * const server = createAgentServer(registry, { ... });
18
+ * await server.start();
19
19
  *
20
- * // Then via @integrations:
21
- * // setup_integration({ provider: 'remote-registry', params: { url: 'https://registry.slash.com', name: 'slash' } })
22
- * // connect_integration({ provider: 'remote-registry', params: { registryId: 'slash', userId: 'user_123' } })
20
+ * registry.register(createRemoteRegistryAgent({
21
+ * secretStore,
22
+ * signJwt: (claims) => server.signJwt(claims),
23
+ * }));
23
24
  * ```
24
25
  */
25
26
  import { defineAgent, defineTool } from "../define.js";
26
- // ============================================
27
- // Helpers
28
- // ============================================
29
- /**
30
- * Make an MCP JSON-RPC call to a remote registry.
31
- */
32
- async function mcpCall(url, token, request) {
33
- const res = await globalThis.fetch(url, {
34
- method: "POST",
35
- headers: {
36
- "Content-Type": "application/json",
37
- Authorization: `Bearer ${token}`,
38
- },
39
- body: JSON.stringify({
40
- jsonrpc: "2.0",
41
- id: Date.now(),
42
- method: "tools/call",
43
- params: {
44
- name: "call_agent",
45
- arguments: {
46
- request: {
47
- action: request.action,
48
- path: request.path,
49
- tool: request.tool,
50
- params: request.params ?? {},
51
- },
52
- },
53
- },
54
- }),
55
- });
56
- if (!res.ok) {
57
- throw new Error(`Registry call failed: ${res.status} ${res.statusText}`);
58
- }
59
- const json = (await res.json());
60
- if (json.error) {
61
- throw new Error(`Registry RPC error: ${json.error.message ?? JSON.stringify(json.error)}`);
62
- }
63
- // Parse the tool result from MCP response
64
- const text = json?.result?.content?.[0]?.text;
65
- if (!text)
66
- return json?.result;
67
- try {
68
- return JSON.parse(text);
69
- }
70
- catch {
71
- return { raw: text };
72
- }
73
- }
74
- /**
75
- * Get an access token from a remote registry via /oauth/token.
76
- */
77
- async function getRegistryToken(url, clientId, clientSecret) {
78
- const tokenUrl = url.replace(/\/$/, "") + "/oauth/token";
79
- const res = await globalThis.fetch(tokenUrl, {
80
- method: "POST",
81
- headers: { "Content-Type": "application/json" },
82
- body: JSON.stringify({
83
- grant_type: "client_credentials",
84
- client_id: clientId,
85
- client_secret: clientSecret,
86
- }),
87
- });
88
- if (!res.ok) {
89
- const body = await res.text();
90
- throw new Error(`Token exchange failed: ${res.status} ${body}`);
91
- }
92
- const json = (await res.json());
93
- return json.access_token;
94
- }
95
- // ============================================
96
- // Create Remote Registry Agent
97
- // ============================================
27
+ const ENTITY_TYPE = "remote-registry-connections";
98
28
  export function createRemoteRegistryAgent(options) {
99
- const { secretStore } = options;
100
- // We store all registry connections as a single JSON blob per owner.
101
- // The secret ID is stored via associate/resolveByEntity for lookup.
102
- const ENTITY_TYPE = "remote-registry-connections";
103
- /**
104
- * Store a registry connection (metadata + credentials).
105
- */
106
- async function storeConnection(ownerId, conn, clientSecret) {
107
- // Load existing connections, update, and store back
29
+ const { secretStore, signJwt, addTrustedIssuer } = options;
30
+ // --- Connection storage (KV via SecretStore) ---
31
+ async function storeConnection(ownerId, conn) {
32
+ console.error("[remote-registry] storeConnection for owner:", ownerId, "conn:", conn.id);
108
33
  const all = await loadAllConnections(ownerId);
109
- all[conn.id] = { ...conn, clientSecret };
34
+ all[conn.id] = conn;
110
35
  const value = JSON.stringify(all);
111
- // Store the blob
112
36
  const scope = { tenantId: ownerId };
113
37
  const secretId = await secretStore.store(value, ownerId);
114
- // Link it so we can find it later
115
38
  if (secretStore.associate) {
116
39
  await secretStore.associate(secretId, ENTITY_TYPE, ownerId, scope);
117
40
  }
118
41
  }
119
- /**
120
- * Load all connections from the stored blob.
121
- */
122
42
  async function loadAllConnections(ownerId) {
123
- // Try resolveByEntity first (v0.7.0+)
43
+ console.error("[remote-registry] loadAllConnections for owner:", ownerId);
124
44
  if (secretStore.resolveByEntity) {
125
45
  const scope = { tenantId: ownerId };
126
46
  const secretIds = await secretStore.resolveByEntity(ENTITY_TYPE, ownerId, scope);
127
- if (secretIds && secretIds.length > 0) {
128
- // Resolve the latest stored blob
129
- const latestId = secretIds[secretIds.length - 1];
130
- const raw = await secretStore.resolve(latestId, ownerId);
47
+ if (secretIds?.length) {
48
+ const raw = await secretStore.resolve(secretIds[secretIds.length - 1], ownerId);
131
49
  if (raw) {
132
50
  try {
133
51
  return JSON.parse(raw);
@@ -140,321 +58,229 @@ export function createRemoteRegistryAgent(options) {
140
58
  }
141
59
  return {};
142
60
  }
143
- /**
144
- * Load a registry connection.
145
- */
146
61
  async function loadConnection(ownerId, registryId) {
147
62
  const all = await loadAllConnections(ownerId);
148
- const entry = all[registryId];
149
- if (!entry)
150
- return null;
151
- const { clientSecret, ...conn } = entry;
152
- return { conn, clientSecret };
63
+ return all[registryId] ?? null;
153
64
  }
154
- /**
155
- * List all registry connections for an owner.
156
- */
157
- async function listConnectionsList(ownerId) {
158
- const all = await loadAllConnections(ownerId);
159
- return Object.values(all).map(({ clientSecret: _, ...conn }) => conn);
65
+ // --- MCP call helper ---
66
+ async function mcpCall(url, jwt, request) {
67
+ const res = await globalThis.fetch(url, {
68
+ method: "POST",
69
+ headers: {
70
+ "Content-Type": "application/json",
71
+ Authorization: `Bearer ${jwt}`,
72
+ },
73
+ body: JSON.stringify({
74
+ jsonrpc: "2.0",
75
+ id: Date.now(),
76
+ method: "tools/call",
77
+ params: { name: "call_agent", arguments: { request } },
78
+ }),
79
+ });
80
+ const rpc = await res.json();
81
+ const text = rpc.result?.content?.[0]?.text;
82
+ return text ? JSON.parse(text) : rpc.result;
160
83
  }
161
- /**
162
- * Get an authenticated token for a registry connection.
163
- */
164
- async function getAuthenticatedToken(ownerId, registryId) {
165
- const data = await loadConnection(ownerId, registryId);
166
- if (!data) {
167
- throw new Error(`No registry connection '${registryId}'. Use setup_integration first.`);
84
+ // --- Proxy: sign JWT and call remote ---
85
+ async function proxyCall(ownerId, registryId, request) {
86
+ const conn = await loadConnection(ownerId, registryId);
87
+ if (!conn) {
88
+ return { success: false, error: `No connection '${registryId}'. Use setup_integration first.` };
168
89
  }
169
- const token = await getRegistryToken(data.conn.url, data.conn.clientId, data.clientSecret);
170
- return { token, conn: data.conn };
90
+ const jwt = await signJwt({ tenantId: conn.remoteTenantId, action: "proxy", type: "agent-registry" });
91
+ const result = await mcpCall(conn.url, jwt, request);
92
+ return { success: true, result };
171
93
  }
172
- // ---- Tools ----
173
- const callRemoteTool = defineTool({
174
- name: "call_remote",
175
- description: "Make an authenticated MCP call to a remote agent registry. " +
176
- "Proxies the request with the stored tenant credentials.",
94
+ // --- Tools ---
95
+ const proxyTool = defineTool({
96
+ name: "proxy_call",
97
+ description: "Proxy an MCP call to a connected remote registry.",
177
98
  visibility: "public",
178
99
  inputSchema: {
179
100
  type: "object",
180
101
  properties: {
181
- registryId: {
182
- type: "string",
183
- description: "Registry connection ID",
184
- },
185
- agentPath: {
186
- type: "string",
187
- description: "Agent path on the remote registry (e.g. '@integrations')",
188
- },
189
- action: {
190
- type: "string",
191
- description: "Action to perform (e.g. 'execute_tool')",
192
- },
193
- tool: {
194
- type: "string",
195
- description: "Tool name to call",
196
- },
197
- params: {
198
- type: "object",
199
- description: "Tool parameters",
200
- },
102
+ registryId: { type: "string", description: "Registry connection ID" },
103
+ action: { type: "string" },
104
+ path: { type: "string" },
105
+ tool: { type: "string" },
106
+ params: { type: "object" },
201
107
  },
202
- required: ["registryId", "agentPath", "action", "tool"],
108
+ required: ["registryId", "action", "path", "tool"],
203
109
  },
204
- execute: async (input, ctx) => {
205
- const { token, conn } = await getAuthenticatedToken(ctx.callerId, input.registryId);
206
- return mcpCall(conn.url, token, {
110
+ execute: async (input, _ctx) => {
111
+ return proxyCall("system", input.registryId, {
207
112
  action: input.action,
208
- path: input.agentPath,
113
+ path: input.path,
209
114
  tool: input.tool,
210
115
  params: input.params,
211
116
  });
212
117
  },
213
118
  });
214
- const listRemoteAgentsTool = defineTool({
215
- name: "list_remote_agents",
216
- description: "List agents available on a remote registry.",
119
+ const listTool = defineTool({
120
+ name: "list_connections",
121
+ description: "List all connected remote registries.",
217
122
  visibility: "public",
218
- inputSchema: {
219
- type: "object",
220
- properties: {
221
- registryId: {
222
- type: "string",
223
- description: "Registry connection ID",
224
- },
225
- },
226
- required: ["registryId"],
123
+ inputSchema: { type: "object", properties: {} },
124
+ execute: async (_input, _ctx) => {
125
+ const all = await loadAllConnections("system");
126
+ return {
127
+ connections: Object.values(all).map(c => ({
128
+ id: c.id,
129
+ name: c.name,
130
+ url: c.url,
131
+ remoteTenantId: c.remoteTenantId,
132
+ })),
133
+ };
227
134
  },
228
- execute: async (input, ctx) => {
229
- const { token, conn } = await getAuthenticatedToken(ctx.callerId, input.registryId);
230
- const listUrl = conn.url.replace(/\/$/, "") + "/list";
231
- const res = await globalThis.fetch(listUrl, {
232
- headers: { Authorization: `Bearer ${token}` },
135
+ });
136
+ // Extract setup/connect as standalone functions to avoid circular reference
137
+ const setupFn = async (params, _ctx) => {
138
+ const url = params.url;
139
+ const name = params.name ?? "registry";
140
+ const oidcUserId = params.oidcUserId;
141
+ if (!url)
142
+ return { success: false, error: "url is required" };
143
+ try {
144
+ const baseUrl = url.replace(/\/$/, "");
145
+ // Phase 2: Complete setup after OIDC — store connection with resolved tenant
146
+ if (oidcUserId) {
147
+ const jwt = await signJwt({ sub: oidcUserId, action: "setup", type: "agent-registry" });
148
+ const tokenRes = await globalThis.fetch(baseUrl + "/oauth/token", {
149
+ method: "POST", headers: { "Content-Type": "application/json" },
150
+ body: JSON.stringify({ grant_type: "jwt_exchange", assertion: jwt, scope: "setup" }),
151
+ });
152
+ const tokenData = await tokenRes.json();
153
+ if (!tokenData.access_token && !tokenData.tenant_id) {
154
+ return { success: false, error: tokenData.error_description ?? tokenData.error ?? "Setup completion failed" };
155
+ }
156
+ const remoteTenantId = tokenData.tenant_id ?? name;
157
+ const ownerId = "system";
158
+ await storeConnection(ownerId, { id: name, name, url: baseUrl, remoteTenantId, createdAt: Date.now() });
159
+ return { success: true, data: { registryId: name, url, remoteTenantId } };
160
+ }
161
+ // Phase 1: Discover JWKS, establish trust, then request OIDC
162
+ const configUrl = baseUrl + "/.well-known/configuration";
163
+ const configRes = await globalThis.fetch(configUrl);
164
+ if (!configRes.ok)
165
+ return { success: false, error: "Failed to discover registry at " + configUrl };
166
+ const remoteConfig = await configRes.json();
167
+ if (remoteConfig.jwks_uri) {
168
+ const jwksRes = await globalThis.fetch(remoteConfig.jwks_uri);
169
+ if (!jwksRes.ok)
170
+ return { success: false, error: "JWKS not reachable" };
171
+ }
172
+ if (addTrustedIssuer)
173
+ await addTrustedIssuer(baseUrl);
174
+ // Request identity — atlas will return authorize URL for Slack OIDC
175
+ const jwt = await signJwt({ action: "setup", type: "agent-registry", targetUrl: url });
176
+ const tokenRes = await globalThis.fetch(baseUrl + "/oauth/token", {
177
+ method: "POST", headers: { "Content-Type": "application/json" },
178
+ body: JSON.stringify({ grant_type: "jwt_exchange", assertion: jwt, scope: "setup" }),
233
179
  });
234
- if (!res.ok) {
235
- throw new Error(`Failed to list agents: ${res.status}`);
180
+ const tokenData = await tokenRes.json();
181
+ // If already set up (user linked), store connection directly
182
+ if (tokenData.access_token) {
183
+ const remoteTenantId = tokenData.tenant_id ?? name;
184
+ const ownerId = "system";
185
+ await storeConnection(ownerId, { id: name, name, url: baseUrl, remoteTenantId, createdAt: Date.now() });
186
+ return { success: true, data: { registryId: name, url, remoteTenantId } };
236
187
  }
237
- return res.json();
238
- },
239
- });
240
- // ---- Agent Definition ----
188
+ // Need OIDC — return authorize URL to caller
189
+ if (tokenData.error === "identity_required") {
190
+ return { success: false, error: "identity_required", data: { authorizeUrl: tokenData.authorize_url, registryId: name, url } };
191
+ }
192
+ return { success: false, error: tokenData.error_description ?? tokenData.error ?? "Unexpected response from token endpoint" };
193
+ }
194
+ catch (err) {
195
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
196
+ }
197
+ };
198
+ const connectFn = async (params, ctx) => {
199
+ const registryId = params.registryId;
200
+ const redirectUri = params.redirectUri ?? "";
201
+ const oidcUserId = params.oidcUserId;
202
+ if (!registryId)
203
+ return { success: false, error: "registryId is required" };
204
+ try {
205
+ const ownerId = "system"; // tenant-scoped
206
+ const conn = await loadConnection(ownerId, registryId);
207
+ if (!conn)
208
+ return { success: false, error: "No connection '" + registryId + "'" };
209
+ // Use OIDC-issued identity if available, never send "anonymous" as sub
210
+ const sub = oidcUserId ?? (ctx.callerId !== "anonymous" ? ctx.callerId : undefined);
211
+ const jwt = await signJwt({ ...(sub ? { sub } : {}), tenantId: conn.remoteTenantId, action: "connect", type: "agent-registry" });
212
+ const tokenRes = await globalThis.fetch(conn.url + "/oauth/token", {
213
+ method: "POST", headers: { "Content-Type": "application/json" },
214
+ body: JSON.stringify({ grant_type: "jwt_exchange", assertion: jwt, redirect_uri: redirectUri }),
215
+ });
216
+ const tokenData = await tokenRes.json();
217
+ if (tokenData.access_token)
218
+ return { success: true, data: { registryId, accessToken: tokenData.access_token, userId: tokenData.user_id, tenantId: tokenData.tenant_id } };
219
+ if (tokenData.error === "identity_required")
220
+ return { success: false, error: "identity_required", data: { authorizeUrl: tokenData.authorize_url, tenantId: tokenData.tenant_id } };
221
+ return { success: false, error: tokenData.error_description ?? tokenData.error ?? "Token exchange failed" };
222
+ }
223
+ catch (err) {
224
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
225
+ }
226
+ };
241
227
  return defineAgent({
242
228
  path: "@remote-registry",
243
229
  entrypoint: "You manage connections to remote agent registries. " +
244
- "Use setup to connect a new registry, connect to register users, " +
245
- "and call_remote to proxy authenticated MCP calls.",
230
+ "Use setup to connect a new registry, connect to link user identities, " +
231
+ "and proxy_call to make authenticated calls.",
246
232
  config: {
247
233
  name: "Remote Registry",
248
- description: "Connect to remote agent registries (MCP over HTTP) for federated integrations",
234
+ description: "Connect to remote agent registries via JWKS trust + jwt_exchange",
249
235
  supportedActions: ["execute_tool", "describe_tools", "load"],
250
- integration: {
251
- provider: "remote-registry",
252
- displayName: "Agent Registry",
253
- icon: "server",
254
- category: "infrastructure",
255
- description: "Connect to a remote agent registry to access its integrations, databases, and agents.",
256
- },
257
236
  },
258
237
  visibility: "public",
259
- integrationMethods: {
260
- async setup(params, ctx) {
261
- const url = params.url;
262
- const name = params.name ?? "registry";
263
- if (!url) {
264
- return { success: false, error: "url is required" };
265
- }
238
+ integration: {
239
+ provider: "remote-registry",
240
+ displayName: "Agent Registry",
241
+ icon: "server",
242
+ category: "infrastructure",
243
+ description: "Connect to a remote agent registry via JWKS trust exchange.",
244
+ setup: (params, ctx) => setupFn(params, ctx),
245
+ connect: (params, ctx) => connectFn(params, ctx),
246
+ async discover(params) {
247
+ const url = params.url ?? "";
266
248
  try {
267
- // 1. Create tenant on remote registry
268
- const setupUrl = url.replace(/\/$/, "") + "/setup";
269
- const setupRes = await globalThis.fetch(setupUrl, {
270
- method: "POST",
271
- headers: { "Content-Type": "application/json" },
272
- body: JSON.stringify({ tenant: name }),
273
- });
274
- if (!setupRes.ok) {
275
- const body = await setupRes.text();
276
- return {
277
- success: false,
278
- error: `Failed to create tenant on registry: ${setupRes.status} ${body}`,
279
- };
280
- }
281
- const setupResult = (await setupRes.json());
282
- if (!setupResult.success || !setupResult.result?.tenantId) {
283
- return {
284
- success: false,
285
- error: "Registry /setup did not return a tenantId",
286
- };
287
- }
288
- const remoteTenantId = setupResult.result.tenantId;
289
- // 2. Register a client for this tenant
290
- // Use the setup token (or root key) to create client credentials
291
- const token = setupResult.result.token;
292
- if (!token) {
293
- return {
294
- success: false,
295
- error: "Registry /setup did not return a token for client creation",
296
- };
297
- }
298
- const registerResult = await mcpCall(url, token, {
299
- action: "execute_tool",
300
- path: "@auth",
301
- tool: "register",
302
- params: {
303
- name: `${name}-client`,
304
- scopes: ["integrations", "secrets", "users"],
305
- },
306
- });
307
- const clientId = registerResult?.clientId ?? registerResult?.result?.clientId;
308
- const clientSecret = registerResult?.clientSecret?.value ??
309
- registerResult?.result?.clientSecret?.value ??
310
- registerResult?.clientSecret;
311
- if (!clientId || !clientSecret) {
312
- return {
313
- success: false,
314
- error: `Failed to register client: ${JSON.stringify(registerResult)}`,
315
- };
316
- }
317
- // 3. Store connection
318
- const conn = {
319
- id: name,
320
- name,
321
- url: url.replace(/\/$/, ""),
322
- remoteTenantId,
323
- clientId,
324
- createdAt: Date.now(),
325
- };
326
- await storeConnection(ctx.callerId, conn, clientSecret);
327
- return {
328
- success: true,
329
- data: {
330
- registryId: name,
331
- url: conn.url,
332
- remoteTenantId,
333
- clientId,
334
- },
335
- };
249
+ const res = await globalThis.fetch(url.replace(/\/$/, "") + "/.well-known/configuration");
250
+ if (!res.ok)
251
+ return { success: false, error: "No configuration endpoint at " + url };
252
+ const config = await res.json();
253
+ return { success: true, data: { url, issuer: config.issuer, grantTypes: config.supported_grant_types, jwksUri: config.jwks_uri } };
336
254
  }
337
255
  catch (err) {
338
- return {
339
- success: false,
340
- error: err instanceof Error ? err.message : String(err),
341
- };
256
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
342
257
  }
343
258
  },
344
- async connect(params, ctx) {
345
- const registryId = params.registryId;
346
- const userId = params.userId ?? ctx.callerId;
347
- if (!registryId) {
348
- return { success: false, error: "registryId is required" };
349
- }
350
- try {
351
- const { token, conn } = await getAuthenticatedToken(ctx.callerId, registryId);
352
- // Register user on the remote registry
353
- const result = await mcpCall(conn.url, token, {
354
- action: "execute_tool",
355
- path: "@users",
356
- tool: "create_user",
357
- params: { name: userId, tenantId: conn.remoteTenantId },
358
- });
359
- return {
360
- success: true,
361
- data: {
362
- registryId,
363
- userId,
364
- remoteUser: result,
365
- },
366
- };
367
- }
368
- catch (err) {
369
- return {
370
- success: false,
371
- error: err instanceof Error ? err.message : String(err),
372
- };
373
- }
374
- },
375
- async list(_params, ctx) {
376
- try {
377
- const conns = await listConnectionsList(ctx.callerId);
378
- return {
379
- success: true,
380
- data: conns.map((c) => ({
381
- id: c.id,
382
- name: c.name,
383
- url: c.url,
384
- remoteTenantId: c.remoteTenantId,
385
- createdAt: c.createdAt,
386
- })),
387
- };
388
- }
389
- catch (err) {
390
- return {
391
- success: false,
392
- error: err instanceof Error ? err.message : String(err),
393
- };
394
- }
259
+ async list() {
260
+ const all = await loadAllConnections("system");
261
+ return { success: true, data: { connections: Object.values(all).map(c => ({ id: c.id, name: c.name, url: c.url, remoteTenantId: c.remoteTenantId })) } };
395
262
  },
396
- async get(params, ctx) {
397
- const registryId = params.registryId;
398
- if (!registryId) {
399
- return { success: false, error: "registryId is required" };
400
- }
401
- try {
402
- const data = await loadConnection(ctx.callerId, registryId);
403
- if (!data) {
404
- return {
405
- success: false,
406
- error: `No registry connection '${registryId}'`,
407
- };
408
- }
409
- return {
410
- success: true,
411
- data: {
412
- id: data.conn.id,
413
- name: data.conn.name,
414
- url: data.conn.url,
415
- remoteTenantId: data.conn.remoteTenantId,
416
- clientId: data.conn.clientId,
417
- createdAt: data.conn.createdAt,
418
- },
419
- };
420
- }
421
- catch (err) {
422
- return {
423
- success: false,
424
- error: err instanceof Error ? err.message : String(err),
425
- };
426
- }
263
+ async get(params) {
264
+ const id = params.registryId ?? "";
265
+ const conn = await loadConnection("system", id);
266
+ if (!conn)
267
+ return { success: false, error: "No connection '" + id + "'" };
268
+ return { success: true, data: conn };
427
269
  },
428
- async update(params, ctx) {
429
- const registryId = params.registryId;
430
- if (!registryId) {
431
- return { success: false, error: "registryId is required" };
432
- }
433
- try {
434
- const data = await loadConnection(ctx.callerId, registryId);
435
- if (!data) {
436
- return {
437
- success: false,
438
- error: `No registry connection '${registryId}'`,
439
- };
440
- }
441
- // Update mutable fields
442
- if (params.name)
443
- data.conn.name = params.name;
444
- if (params.url)
445
- data.conn.url = params.url.replace(/\/$/, "");
446
- await storeConnection(ctx.callerId, data.conn, data.clientSecret);
447
- return { success: true, data: { id: data.conn.id, updated: true } };
448
- }
449
- catch (err) {
450
- return {
451
- success: false,
452
- error: err instanceof Error ? err.message : String(err),
453
- };
454
- }
270
+ async update(params) {
271
+ const id = params.registryId ?? "";
272
+ const conn = await loadConnection("system", id);
273
+ if (!conn)
274
+ return { success: false, error: "No connection '" + id + "'" };
275
+ if (params.name)
276
+ conn.name = params.name;
277
+ if (params.url)
278
+ conn.url = params.url;
279
+ await storeConnection("system", conn);
280
+ return { success: true, data: conn };
455
281
  },
456
282
  },
457
- tools: [callRemoteTool, listRemoteAgentsTool],
283
+ tools: [proxyTool, listTool],
458
284
  });
459
285
  }
460
286
  //# sourceMappingURL=remote-registry.js.map