@slashfi/agents-sdk 0.34.1 → 0.36.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 (91) hide show
  1. package/README.md +2 -1
  2. package/dist/agent-definitions/auth.d.ts +3 -3
  3. package/dist/agent-definitions/auth.d.ts.map +1 -1
  4. package/dist/agent-definitions/auth.js +10 -4
  5. package/dist/agent-definitions/auth.js.map +1 -1
  6. package/dist/agent-definitions/config.d.ts.map +1 -1
  7. package/dist/agent-definitions/config.js.map +1 -1
  8. package/dist/agent-definitions/integrations.d.ts +12 -3
  9. package/dist/agent-definitions/integrations.d.ts.map +1 -1
  10. package/dist/agent-definitions/integrations.js +35 -16
  11. package/dist/agent-definitions/integrations.js.map +1 -1
  12. package/dist/agent-definitions/remote-registry.d.ts.map +1 -1
  13. package/dist/agent-definitions/remote-registry.js +17 -22
  14. package/dist/agent-definitions/remote-registry.js.map +1 -1
  15. package/dist/agent-definitions/users.d.ts.map +1 -1
  16. package/dist/agent-definitions/users.js.map +1 -1
  17. package/dist/auth-governance.js.map +1 -1
  18. package/dist/call-agent-schema.d.ts.map +1 -1
  19. package/dist/call-agent-schema.js +1 -2
  20. package/dist/call-agent-schema.js.map +1 -1
  21. package/dist/cjs/agent-definitions/auth.js +10 -4
  22. package/dist/cjs/agent-definitions/auth.js.map +1 -1
  23. package/dist/cjs/agent-definitions/config.js.map +1 -1
  24. package/dist/cjs/agent-definitions/integrations.js +35 -16
  25. package/dist/cjs/agent-definitions/integrations.js.map +1 -1
  26. package/dist/cjs/agent-definitions/remote-registry.js +17 -22
  27. package/dist/cjs/agent-definitions/remote-registry.js.map +1 -1
  28. package/dist/cjs/agent-definitions/users.js.map +1 -1
  29. package/dist/cjs/auth-governance.js.map +1 -1
  30. package/dist/cjs/call-agent-schema.js +1 -2
  31. package/dist/cjs/call-agent-schema.js.map +1 -1
  32. package/dist/cjs/define.js.map +1 -1
  33. package/dist/cjs/events.js.map +1 -1
  34. package/dist/cjs/index.js +6 -2
  35. package/dist/cjs/index.js.map +1 -1
  36. package/dist/cjs/key-manager.js.map +1 -1
  37. package/dist/cjs/registry-consumer.js +60 -15
  38. package/dist/cjs/registry-consumer.js.map +1 -1
  39. package/dist/cjs/registry.js +61 -11
  40. package/dist/cjs/registry.js.map +1 -1
  41. package/dist/cjs/server.js +143 -192
  42. package/dist/cjs/server.js.map +1 -1
  43. package/dist/cjs/types.js +13 -0
  44. package/dist/cjs/types.js.map +1 -1
  45. package/dist/define.d.ts.map +1 -1
  46. package/dist/define.js.map +1 -1
  47. package/dist/events.d.ts +85 -9
  48. package/dist/events.d.ts.map +1 -1
  49. package/dist/events.js.map +1 -1
  50. package/dist/index.d.ts +3 -2
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +1 -0
  53. package/dist/index.js.map +1 -1
  54. package/dist/key-manager.d.ts.map +1 -1
  55. package/dist/key-manager.js +1 -1
  56. package/dist/key-manager.js.map +1 -1
  57. package/dist/registry-consumer.d.ts +8 -8
  58. package/dist/registry-consumer.d.ts.map +1 -1
  59. package/dist/registry-consumer.js +60 -15
  60. package/dist/registry-consumer.js.map +1 -1
  61. package/dist/registry.d.ts +16 -1
  62. package/dist/registry.d.ts.map +1 -1
  63. package/dist/registry.js +61 -11
  64. package/dist/registry.js.map +1 -1
  65. package/dist/server.d.ts +1 -1
  66. package/dist/server.d.ts.map +1 -1
  67. package/dist/server.js +136 -185
  68. package/dist/server.js.map +1 -1
  69. package/dist/types.d.ts +38 -0
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/types.js +10 -1
  72. package/dist/types.js.map +1 -1
  73. package/package.json +1 -1
  74. package/src/agent-definitions/auth.ts +31 -14
  75. package/src/agent-definitions/config.ts +4 -4
  76. package/src/agent-definitions/integrations.ts +119 -63
  77. package/src/agent-definitions/remote-registry.ts +65 -38
  78. package/src/agent-definitions/users.ts +36 -3
  79. package/src/auth-governance.ts +2 -2
  80. package/src/call-agent-schema.test.ts +4 -1
  81. package/src/call-agent-schema.ts +4 -3
  82. package/src/consumer.test.ts +4 -1
  83. package/src/define.ts +18 -12
  84. package/src/events.ts +83 -9
  85. package/src/hooks.test.ts +439 -0
  86. package/src/index.ts +14 -1
  87. package/src/key-manager.ts +9 -2
  88. package/src/registry-consumer.ts +85 -24
  89. package/src/registry.ts +88 -13
  90. package/src/server.ts +215 -239
  91. package/src/types.ts +62 -0
@@ -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<any> {
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 any;
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<any> {
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: any, _ctx: ToolContext) => {
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: any, _ctx: ToolContext) => {
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: any, _ctx: ToolContext) => {
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: IntegrationMethodContext,
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 any;
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: Discover JWKS, establish trust, then request OIDC
323
- const configUrl = `${baseUrl}/.well-known/configuration`;
324
- console.log("[setupFn] fetching config:", configUrl);
325
- const configRes = await globalThis.fetch(configUrl);
326
- console.log("[setupFn] config status:", configRes.status);
327
- if (!configRes.ok)
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: `Failed to discover registry at ${configUrl}`,
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 any;
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: IntegrationMethodContext,
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 any;
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 as any),
502
- connect: (params, ctx) => connectFn(params, ctx as any),
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 res = await globalThis.fetch(
507
- `${url.replace(/\/$/, "")}/.well-known/configuration`,
508
- );
509
- if (!res.ok)
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: `No configuration endpoint at ${url}`,
539
+ error: `JWKS not reachable at ${jwksUri}`,
513
540
  };
514
- const config = (await res.json()) as any;
541
+ }
515
542
  return {
516
543
  success: true,
517
544
  data: {
518
545
  url,
519
- issuer: config.issuer,
520
- grantTypes: config.supported_grant_types,
521
- jwksUri: config.jwks_uri,
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 any[],
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: any, __ctx: ToolContext) => {
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: any, __ctx: ToolContext) => {
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: any, __ctx: ToolContext) => {
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,
@@ -39,7 +39,7 @@ export function canSeeAgent(
39
39
  agent: AgentDefinition,
40
40
  auth: ResolvedAuth | null,
41
41
  ): boolean {
42
- const visibility = ((agent as any).visibility ??
42
+ const visibility = (agent.visibility ??
43
43
  agent.config?.visibility ??
44
44
  "internal") as Visibility;
45
45
  if (hasAdminScope(auth)) return true;
@@ -87,7 +87,7 @@ export function getVisibleTools(
87
87
  agent: AgentDefinition,
88
88
  auth: ResolvedAuth | null,
89
89
  ): typeof agent.tools {
90
- const agentVisibility = ((agent as any).visibility ??
90
+ const agentVisibility = (agent.visibility ??
91
91
  agent.config?.visibility ??
92
92
  "internal") as Visibility;
93
93
  return agent.tools.filter((t) => canSeeTool(t, auth, agentVisibility));
@@ -293,7 +293,10 @@ describe("zodToOpenAiJsonSchema", () => {
293
293
  required: z.string(),
294
294
  optional: z.string().optional(),
295
295
  });
296
- const jsonSchema = zodToOpenAiJsonSchema(schema) as any;
296
+ const jsonSchema = zodToOpenAiJsonSchema(schema) as Record<
297
+ string,
298
+ unknown
299
+ >;
297
300
 
298
301
  // Required field should be in required array
299
302
  expect(jsonSchema.required).toContain("required");
@@ -101,8 +101,7 @@ function normalizeJsonSchema(schema: Record<string, unknown>): Record<string, un
101
101
  export function zodToOpenAiJsonSchema(
102
102
  schema: ZodTypeAny,
103
103
  ): Record<string, unknown> {
104
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
105
- const raw = zodToJsonSchema(schema as any, { target: "openAi" }) as Record<
104
+ const raw = zodToJsonSchema(schema, { target: "openAi" }) as Record<
106
105
  string,
107
106
  unknown
108
107
  >;
@@ -170,7 +169,9 @@ export const describeToolsActionSchema = callAgentBaseSchema.extend({
170
169
  tools: z
171
170
  .array(z.string())
172
171
  .optional()
173
- .describe("Optional: filter to specific tool names. Omit to list all."),
172
+ .describe(
173
+ "Optional filter: specific tool names. Use undefined / omit for all tools; do not use [] (empty array is not 'all tools').",
174
+ ),
174
175
  });
175
176
 
176
177
  /** Load: get agent definition */
@@ -70,7 +70,7 @@ describe("Registry Consumer E2E", () => {
70
70
  await server.stop();
71
71
  });
72
72
 
73
- test("discover registry via .well-known/configuration", async () => {
73
+ test("discover registry via MCP initialize", async () => {
74
74
  const config = {
75
75
  registries: [`http://localhost:${PORT}`],
76
76
  };
@@ -80,6 +80,9 @@ describe("Registry Consumer E2E", () => {
80
80
 
81
81
  expect(discovery).toBeDefined();
82
82
  expect(discovery.issuer).toBeDefined();
83
+ expect(discovery.token_endpoint).toBe(
84
+ `http://localhost:${PORT}/oauth/token`,
85
+ );
83
86
  });
84
87
 
85
88
  test("list agents from registry", async () => {
package/src/define.ts CHANGED
@@ -240,8 +240,9 @@ export function defineAgent<TContext extends ToolContext = ToolContext>(
240
240
  config: { type: "object" },
241
241
  },
242
242
  },
243
- execute: (input: any, ctx: any) => fn(input, ctx),
244
- }) as any,
243
+ execute: (input: Record<string, unknown>, ctx: ToolContext) =>
244
+ fn(input, ctx),
245
+ }) as ToolDefinition<ToolContext>,
245
246
  );
246
247
  }
247
248
  if (h.connect) {
@@ -260,8 +261,9 @@ export function defineAgent<TContext extends ToolContext = ToolContext>(
260
261
  },
261
262
  required: ["registryId"] as const,
262
263
  },
263
- execute: (input: any, ctx: any) => fn(input, ctx),
264
- }) as any,
264
+ execute: (input: Record<string, unknown>, ctx: ToolContext) =>
265
+ fn(input, ctx),
266
+ }) as ToolDefinition<ToolContext>,
265
267
  );
266
268
  }
267
269
  if (h.discover) {
@@ -275,8 +277,9 @@ export function defineAgent<TContext extends ToolContext = ToolContext>(
275
277
  type: "object" as const,
276
278
  properties: { url: { type: "string" } },
277
279
  },
278
- execute: (input: any, ctx: any) => fn(input, ctx),
279
- }) as any,
280
+ execute: (input: Record<string, unknown>, ctx: ToolContext) =>
281
+ fn(input, ctx),
282
+ }) as ToolDefinition<ToolContext>,
280
283
  );
281
284
  }
282
285
  if (h.list) {
@@ -287,8 +290,9 @@ export function defineAgent<TContext extends ToolContext = ToolContext>(
287
290
  description: `List connected ${h.displayName} instances.`,
288
291
  visibility: "public" as const,
289
292
  inputSchema: { type: "object" as const, properties: {} },
290
- execute: (input: any, ctx: any) => fn(input, ctx),
291
- }) as any,
293
+ execute: (input: Record<string, unknown>, ctx: ToolContext) =>
294
+ fn(input, ctx),
295
+ }) as ToolDefinition<ToolContext>,
292
296
  );
293
297
  }
294
298
  if (h.get) {
@@ -303,8 +307,9 @@ export function defineAgent<TContext extends ToolContext = ToolContext>(
303
307
  properties: { registryId: { type: "string" } },
304
308
  required: ["registryId"] as const,
305
309
  },
306
- execute: (input: any, ctx: any) => fn(input, ctx),
307
- }) as any,
310
+ execute: (input: Record<string, unknown>, ctx: ToolContext) =>
311
+ fn(input, ctx),
312
+ }) as ToolDefinition<ToolContext>,
308
313
  );
309
314
  }
310
315
  if (h.update) {
@@ -323,8 +328,9 @@ export function defineAgent<TContext extends ToolContext = ToolContext>(
323
328
  },
324
329
  required: ["registryId"] as const,
325
330
  },
326
- execute: (input: any, ctx: any) => fn(input, ctx),
327
- }) as any,
331
+ execute: (input: Record<string, unknown>, ctx: ToolContext) =>
332
+ fn(input, ctx),
333
+ }) as ToolDefinition<ToolContext>,
328
334
  );
329
335
  }
330
336
  }
package/src/events.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  * Filtering happens in the callback, not the API.
11
11
  */
12
12
 
13
- import type { CallAgentRequest, CallAgentResponse } from "./types.js";
13
+ import type { AgentDefinition, CallAgentRequest, CallAgentResponse } from "./types.js";
14
14
  // =============================================================================
15
15
  // Event Types
16
16
  // =============================================================================
@@ -24,7 +24,8 @@ export type SystemEventType =
24
24
  | "tool/error"
25
25
  | "step"
26
26
  | "invoke"
27
- | "call";
27
+ | "tools/call/call_agent"
28
+ | "tools/call/list_agents";
28
29
 
29
30
  /**
30
31
  * Augmentable map for custom event types. Consumers extend this
@@ -124,12 +125,28 @@ export interface InvokeEvent extends BaseEvent {
124
125
  }
125
126
 
126
127
  /**
127
- * Event emitted when a call_agent request is received.
128
- * Call `resolve(response)` to short-circuit the default handler.
129
- * If no listener resolves, the default call handler runs.
128
+ * Event emitted when the `call_agent` MCP tool is invoked.
129
+ * Replaces the legacy `call` event with a namespaced type.
130
+ *
131
+ * Call `next()` to run the default call handler (optionally with a modified request).
132
+ * Call `resolve(response)` to short-circuit with a custom response.
133
+ * If neither is called, the default handler runs.
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * registry.on('tools/call/call_agent', async (event) => {
138
+ * // Proxy to a remote registry
139
+ * if (isRemoteAgent(event.request.path)) {
140
+ * const result = await proxyToRemote(event.request);
141
+ * event.resolve(result);
142
+ * return;
143
+ * }
144
+ * // Fall through to default handler
145
+ * });
146
+ * ```
130
147
  */
131
- export interface CallEvent extends BaseEvent {
132
- type: "call";
148
+ export interface CallAgentToolCallEvent extends BaseEvent {
149
+ type: "tools/call/call_agent";
133
150
  /** The incoming call_agent request */
134
151
  request: CallAgentRequest;
135
152
  /** Run the default call handler and return its result.
@@ -139,6 +156,61 @@ export interface CallEvent extends BaseEvent {
139
156
  resolve(response: CallAgentResponse): void;
140
157
  }
141
158
 
159
+ /**
160
+ * Result shape for list_agents responses.
161
+ */
162
+ export interface ListAgentsResult {
163
+ success: true;
164
+ total: number;
165
+ nextCursor?: string;
166
+ agents: Array<{
167
+ path: string;
168
+ name?: string;
169
+ description?: string;
170
+ supportedActions?: string[];
171
+ integration?: unknown;
172
+ security?: { type: string };
173
+ resources?: Array<{ uri: string; name?: string; mimeType?: string }>;
174
+ tools: string[];
175
+ }>;
176
+ }
177
+
178
+ /**
179
+ * Event emitted when the `list_agents` MCP tool is invoked.
180
+ *
181
+ * Gives hosts a chance to inject additional agents (e.g., from remote registries
182
+ * or consumer config) before BM25 search and pagination run.
183
+ *
184
+ * Call `next(additionalAgents?)` to continue default behavior with optional
185
+ * extra agents merged into the base set.
186
+ * Call `resolve(result)` to short-circuit with a fully formed response.
187
+ * If neither is called, the default handler runs with the base agents.
188
+ *
189
+ * @example
190
+ * ```ts
191
+ * registry.on('tools/call/list_agents', async (event) => {
192
+ * const remoteAgents = await fetchRemoteAgents();
193
+ * await event.next(remoteAgents);
194
+ * });
195
+ * ```
196
+ */
197
+ export interface ListAgentsToolCallEvent extends BaseEvent {
198
+ type: "tools/call/list_agents";
199
+ /** Agents from the local registry (before search/pagination) */
200
+ baseAgents: AgentDefinition[];
201
+ /** Search query, if provided */
202
+ query?: string;
203
+ /** Requested page size */
204
+ limit?: number;
205
+ /** Pagination cursor */
206
+ cursor?: string;
207
+ /** Continue with default BM25/pagination behavior.
208
+ * Pass additional agents to merge into the base set. */
209
+ next(additionalAgents?: AgentDefinition[]): Promise<ListAgentsResult>;
210
+ /** Short-circuit with a complete response (same shape as list_agents output) */
211
+ resolve(result: ListAgentsResult): void;
212
+ }
213
+
142
214
  /**
143
215
  * Union of all built-in event types.
144
216
  */
@@ -148,7 +220,8 @@ export type AgentEvent =
148
220
  | ToolErrorEvent
149
221
  | StepEvent
150
222
  | InvokeEvent
151
- | CallEvent;
223
+ | CallAgentToolCallEvent
224
+ | ListAgentsToolCallEvent;
152
225
 
153
226
  /**
154
227
  * Map from system event type string to event interface.
@@ -159,7 +232,8 @@ export interface SystemEventMap {
159
232
  "tool/error": ToolErrorEvent;
160
233
  step: StepEvent;
161
234
  invoke: InvokeEvent;
162
- call: CallEvent;
235
+ "tools/call/call_agent": CallAgentToolCallEvent;
236
+ "tools/call/list_agents": ListAgentsToolCallEvent;
163
237
  }
164
238
 
165
239
  /**