@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.
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 +128 -135
  19. package/dist/call-agent-schema.d.ts.map +1 -1
  20. package/dist/call-agent-schema.js +35 -5
  21. package/dist/call-agent-schema.js.map +1 -1
  22. package/dist/cjs/agent-definitions/auth.js +10 -4
  23. package/dist/cjs/agent-definitions/auth.js.map +1 -1
  24. package/dist/cjs/agent-definitions/config.js.map +1 -1
  25. package/dist/cjs/agent-definitions/integrations.js +35 -16
  26. package/dist/cjs/agent-definitions/integrations.js.map +1 -1
  27. package/dist/cjs/agent-definitions/remote-registry.js +17 -22
  28. package/dist/cjs/agent-definitions/remote-registry.js.map +1 -1
  29. package/dist/cjs/agent-definitions/users.js.map +1 -1
  30. package/dist/cjs/auth-governance.js.map +1 -1
  31. package/dist/cjs/call-agent-schema.js +35 -5
  32. package/dist/cjs/call-agent-schema.js.map +1 -1
  33. package/dist/cjs/define.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 +3 -1
  40. package/dist/cjs/registry.js.map +1 -1
  41. package/dist/cjs/server.js +70 -121
  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/cjs/validate.js +2 -2
  46. package/dist/cjs/validate.js.map +1 -1
  47. package/dist/define.d.ts.map +1 -1
  48. package/dist/define.js.map +1 -1
  49. package/dist/index.d.ts +2 -1
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1 -0
  52. package/dist/index.js.map +1 -1
  53. package/dist/key-manager.d.ts.map +1 -1
  54. package/dist/key-manager.js +1 -1
  55. package/dist/key-manager.js.map +1 -1
  56. package/dist/registry-consumer.d.ts +8 -8
  57. package/dist/registry-consumer.d.ts.map +1 -1
  58. package/dist/registry-consumer.js +60 -15
  59. package/dist/registry-consumer.js.map +1 -1
  60. package/dist/registry.d.ts.map +1 -1
  61. package/dist/registry.js +3 -1
  62. package/dist/registry.js.map +1 -1
  63. package/dist/server.d.ts +1 -1
  64. package/dist/server.d.ts.map +1 -1
  65. package/dist/server.js +63 -114
  66. package/dist/server.js.map +1 -1
  67. package/dist/types.d.ts +38 -0
  68. package/dist/types.d.ts.map +1 -1
  69. package/dist/types.js +10 -1
  70. package/dist/types.js.map +1 -1
  71. package/dist/validate.d.ts +15 -15
  72. package/dist/validate.js +2 -2
  73. package/dist/validate.js.map +1 -1
  74. package/package.json +1 -1
  75. package/src/agent-definitions/auth.ts +31 -14
  76. package/src/agent-definitions/config.ts +4 -4
  77. package/src/agent-definitions/integrations.ts +119 -63
  78. package/src/agent-definitions/remote-registry.ts +65 -38
  79. package/src/agent-definitions/users.ts +36 -3
  80. package/src/auth-governance.ts +2 -2
  81. package/src/call-agent-schema.test.ts +4 -1
  82. package/src/call-agent-schema.ts +39 -5
  83. package/src/consumer.test.ts +4 -1
  84. package/src/define.ts +18 -12
  85. package/src/index.ts +11 -0
  86. package/src/key-manager.ts +9 -2
  87. package/src/registry-consumer.ts +85 -24
  88. package/src/registry.ts +3 -1
  89. package/src/server.ts +122 -153
  90. package/src/types.ts +62 -0
  91. package/src/validate.ts +2 -2
@@ -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");
@@ -66,14 +66,46 @@ export function nullTolerant<T extends ZodTypeAny>(schema: T) {
66
66
  * This is the standard way to generate input schemas for MCP tools
67
67
  * that will be called by LLMs.
68
68
  */
69
+ /**
70
+ * Recursively normalize a JSON schema for Anthropic compatibility.
71
+ * Replaces `additionalProperties: {}` (empty schema = any) with `true`,
72
+ * which is semantically equivalent but accepted by Anthropic's validator.
73
+ */
74
+ function normalizeJsonSchema(schema: Record<string, unknown>): Record<string, unknown> {
75
+ if (typeof schema !== "object" || schema === null) return schema;
76
+
77
+ const result: Record<string, unknown> = {};
78
+ for (const [key, value] of Object.entries(schema)) {
79
+ if (
80
+ key === "additionalProperties" &&
81
+ typeof value === "object" &&
82
+ value !== null &&
83
+ Object.keys(value).length === 0
84
+ ) {
85
+ result[key] = true;
86
+ } else if (Array.isArray(value)) {
87
+ result[key] = value.map((item) =>
88
+ typeof item === "object" && item !== null
89
+ ? normalizeJsonSchema(item as Record<string, unknown>)
90
+ : item,
91
+ );
92
+ } else if (typeof value === "object" && value !== null) {
93
+ result[key] = normalizeJsonSchema(value as Record<string, unknown>);
94
+ } else {
95
+ result[key] = value;
96
+ }
97
+ }
98
+ return result;
99
+ }
100
+
69
101
  export function zodToOpenAiJsonSchema(
70
102
  schema: ZodTypeAny,
71
103
  ): Record<string, unknown> {
72
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
- return zodToJsonSchema(schema as any, { target: "openAi" }) as Record<
104
+ const raw = zodToJsonSchema(schema, { target: "openAi" }) as Record<
74
105
  string,
75
106
  unknown
76
107
  >;
108
+ return normalizeJsonSchema(raw);
77
109
  }
78
110
 
79
111
  // ─────────────────────────────────────────────────────────────────────────────
@@ -86,7 +118,7 @@ const callAgentBaseSchema = z.object({
86
118
  path: z.string().describe("Agent path (e.g., '@my-agent')"),
87
119
  callerId: z.string().optional().describe("Caller ID for access control"),
88
120
  callerType: callerTypeSchema.optional().describe("Caller type"),
89
- metadata: z.record(z.unknown()).optional().describe("Additional metadata"),
121
+ metadata: z.object({}).passthrough().optional().describe("Additional metadata"),
90
122
  });
91
123
 
92
124
  // ─────────────────────────────────────────────────────────────────────────────
@@ -126,7 +158,7 @@ export const executeToolActionSchema = callAgentBaseSchema.extend({
126
158
  action: z.literal("execute_tool"),
127
159
  tool: z.string().describe("Tool name to call"),
128
160
  params: z
129
- .record(z.unknown())
161
+ .object({}).passthrough()
130
162
  .optional()
131
163
  .describe("Parameters for the tool"),
132
164
  });
@@ -137,7 +169,9 @@ export const describeToolsActionSchema = callAgentBaseSchema.extend({
137
169
  tools: z
138
170
  .array(z.string())
139
171
  .optional()
140
- .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
+ ),
141
175
  });
142
176
 
143
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/index.ts CHANGED
@@ -90,12 +90,23 @@ export type {
90
90
  SecurityScheme,
91
91
  AgentResource,
92
92
  SecuritySchemeSummary,
93
+ AuthClientCredentialsTokenResult,
94
+ AuthSecretValue,
95
+ ExchangeTokenLinkedSuccess,
96
+ ExchangeTokenNeedsIdentity,
97
+ ExchangeTokenRejected,
98
+ ExchangeTokenToolResult,
93
99
  IntegrationMethods,
94
100
  IntegrationMethodResult,
95
101
  IntegrationMethodContext,
96
102
  IntegrationHooks,
97
103
  Visibility,
98
104
  } from "./types.js";
105
+ export {
106
+ isCallAgentErrorResponse,
107
+ isExchangeTokenLinkedSuccess,
108
+ isExchangeTokenNeedsIdentity,
109
+ } from "./types.js";
99
110
 
100
111
  // Define functions
101
112
  export { defineAgent, defineTool } from "./define.js";
@@ -13,7 +13,14 @@
13
13
  * - Signs JWTs with the active key
14
14
  */
15
15
 
16
- import { type JWK, SignJWT, exportJWK, generateKeyPair, importJWK } from "jose";
16
+ import {
17
+ type JWK,
18
+ type JWTPayload,
19
+ SignJWT,
20
+ exportJWK,
21
+ generateKeyPair,
22
+ importJWK,
23
+ } from "jose";
17
24
 
18
25
  // ── Types ──
19
26
 
@@ -236,7 +243,7 @@ export async function createKeyManager(
236
243
 
237
244
  async signJwt(claims: Record<string, unknown>): Promise<string> {
238
245
  const key = getActiveKey();
239
- let builder = new SignJWT({ ...claims } as any)
246
+ let builder = new SignJWT({ ...claims } as JWTPayload)
240
247
  .setProtectedHeader({ alg: ALG, kid: key.kid })
241
248
  .setIssuer(issuer)
242
249
  .setIssuedAt();
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Registry Consumer — Connects to registries and resolves refs.
3
3
  *
4
- * The consumer reads a `ConsumerConfig`, discovers registries via
5
- * `/.well-known/configuration`, resolves refs to agent definitions,
6
- * and provides a unified interface for calling tools across all connected agents.
4
+ * The consumer reads a `ConsumerConfig`, connects to each registry at its MCP URL,
5
+ * resolves refs to agent definitions, and provides a unified interface for calling
6
+ * tools across all connected agents. Registry metadata comes from the MCP
7
+ * `initialize` handshake (`discover()`).
7
8
  *
8
9
  * @example
9
10
  * ```typescript
@@ -181,16 +182,15 @@ function buildRegistryAuthHeaders(
181
182
  // Registry Discovery Types
182
183
  // ============================================
183
184
 
184
- /** Registry well-known configuration (from /.well-known/configuration) */
185
+ /**
186
+ * Registry configuration derived from the MCP `initialize` response and the registry
187
+ * URL (OAuth/JWKS paths follow the usual layout under `issuer`).
188
+ */
185
189
  export interface RegistryConfiguration {
186
190
  issuer: string;
187
191
  jwks_uri?: string;
188
192
  token_endpoint?: string;
189
- agents_endpoint?: string;
190
- call_endpoint?: string;
191
193
  supported_grant_types?: string[];
192
- /** @deprecated Use agents_endpoint + GET /list instead */
193
- agents?: string[];
194
194
  }
195
195
 
196
196
  /** An agent definition as listed by a registry */
@@ -367,6 +367,73 @@ async function listFromMcpServer(
367
367
  }];
368
368
  }
369
369
 
370
+ function issuerFromMcpUrlAndServerInfo(
371
+ serverUrl: string,
372
+ serverInfo?: { name?: string },
373
+ ): string {
374
+ const name = serverInfo?.name;
375
+ if (name && /^https?:\/\//.test(name)) {
376
+ return name.replace(/\/$/, "");
377
+ }
378
+ return new URL(serverUrl).origin;
379
+ }
380
+
381
+ /**
382
+ * Load registry OAuth-facing metadata via MCP initialize (same URL as tools/call).
383
+ */
384
+ async function discoverRegistryViaMcp(
385
+ registryUrl: string,
386
+ authHeaders: Record<string, string>,
387
+ fetchFn: typeof globalThis.fetch,
388
+ ): Promise<RegistryConfiguration> {
389
+ const serverUrl = registryUrl.replace(/\/$/, "");
390
+ const headers: Record<string, string> = {
391
+ "Content-Type": "application/json",
392
+ ...authHeaders,
393
+ };
394
+
395
+ let reqId = 0;
396
+ async function rpc(method: string, params?: Record<string, unknown>) {
397
+ const res = await fetchFn(serverUrl, {
398
+ method: "POST",
399
+ headers,
400
+ body: JSON.stringify({
401
+ jsonrpc: "2.0",
402
+ id: ++reqId,
403
+ method,
404
+ ...(params && { params }),
405
+ }),
406
+ });
407
+ if (!res.ok) {
408
+ throw new Error(`MCP initialize to ${serverUrl} failed: ${res.status}`);
409
+ }
410
+ const json = (await res.json()) as { result?: unknown; error?: { message: string } };
411
+ if (json.error) {
412
+ throw new Error(`MCP RPC error: ${json.error.message}`);
413
+ }
414
+ return json.result;
415
+ }
416
+
417
+ const initResult = (await rpc("initialize", {
418
+ protocolVersion: "2024-11-05",
419
+ capabilities: {},
420
+ clientInfo: { name: "agents-sdk-consumer", version: "1.0.0" },
421
+ })) as {
422
+ serverInfo?: { name?: string; version?: string };
423
+ };
424
+
425
+ await rpc("notifications/initialized").catch(() => {});
426
+
427
+ const issuer = issuerFromMcpUrlAndServerInfo(serverUrl, initResult?.serverInfo);
428
+
429
+ return {
430
+ issuer,
431
+ jwks_uri: `${issuer}/.well-known/jwks.json`,
432
+ token_endpoint: `${issuer}/oauth/token`,
433
+ supported_grant_types: ["client_credentials", "jwt_exchange"],
434
+ };
435
+ }
436
+
370
437
  /**
371
438
  * Call a tool on a direct MCP server.
372
439
  */
@@ -571,17 +638,15 @@ export async function createRegistryConsumer(
571
638
  const cached = discoveryCache.get(registryUrl);
572
639
  if (cached) return cached;
573
640
 
574
- const url = `${registryUrl.replace(/\/$/, "")}/.well-known/configuration`;
575
- const headers: Record<string, string> = registry
641
+ const authHeaders = registry
576
642
  ? buildRegistryAuthHeaders(registry, options.token)
577
643
  : (options.token ? { Authorization: `Bearer ${options.token}` } : {});
578
- const res = await fetchFn(url, { headers });
579
- if (!res.ok) {
580
- throw new Error(
581
- `Failed to discover registry ${registryUrl}: ${res.status}`,
582
- );
583
- }
584
- const configuration = (await res.json()) as RegistryConfiguration;
644
+
645
+ const configuration = await discoverRegistryViaMcp(
646
+ registryUrl,
647
+ authHeaders,
648
+ fetchFn,
649
+ );
585
650
  discoveryCache.set(registryUrl, configuration);
586
651
  return configuration;
587
652
  }
@@ -591,9 +656,7 @@ export async function createRegistryConsumer(
591
656
  registry: ResolvedRegistry,
592
657
  query?: string,
593
658
  ): Promise<AgentListing[]> {
594
- const configuration = await discover(registry.url, registry);
595
- const mcpUrl =
596
- configuration.call_endpoint ?? registry.url.replace(/\/$/, "");
659
+ const mcpUrl = registry.url.replace(/\/$/, "");
597
660
 
598
661
  const response = await callMcpTool(
599
662
  mcpUrl,
@@ -624,9 +687,7 @@ export async function createRegistryConsumer(
624
687
  registry: ResolvedRegistry,
625
688
  request: CallAgentRequest,
626
689
  ): Promise<unknown> {
627
- const configuration = await discover(registry.url, registry);
628
- const mcpUrl =
629
- configuration.call_endpoint ?? registry.url.replace(/\/$/, "");
690
+ const mcpUrl = registry.url.replace(/\/$/, "");
630
691
 
631
692
  const headers: Record<string, string> = {
632
693
  "Content-Type": "application/json",
@@ -840,7 +901,7 @@ export async function createRegistryConsumer(
840
901
  const data = (await callRegistry(registry, {
841
902
  action: "describe_tools",
842
903
  path: agentPath,
843
- tools: [],
904
+ tools: undefined,
844
905
  })) as { tools?: unknown[]; description?: string } | null;
845
906
  if (!data) return null;
846
907
  return {
package/src/registry.ts CHANGED
@@ -707,7 +707,9 @@ export function createAgentRegistry(
707
707
  ),
708
708
  )
709
709
  .filter((t: ToolDefinition) =>
710
- request.tools ? request.tools.includes(t.name) : true,
710
+ request.tools && request.tools.length > 0
711
+ ? request.tools.includes(t.name)
712
+ : true,
711
713
  )
712
714
  .map((t: ToolDefinition) => ({
713
715
  name: t.name,