@slashfi/agents-sdk 0.34.1 → 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.map +1 -1
- package/dist/call-agent-schema.js +1 -2
- 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 +1 -2
- 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/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/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 +4 -3
- 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
|
@@ -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,
|
package/src/auth-governance.ts
CHANGED
|
@@ -39,7 +39,7 @@ export function canSeeAgent(
|
|
|
39
39
|
agent: AgentDefinition,
|
|
40
40
|
auth: ResolvedAuth | null,
|
|
41
41
|
): boolean {
|
|
42
|
-
const 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 = (
|
|
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
|
|
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");
|
package/src/call-agent-schema.ts
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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 */
|
package/src/consumer.test.ts
CHANGED
|
@@ -70,7 +70,7 @@ describe("Registry Consumer E2E", () => {
|
|
|
70
70
|
await server.stop();
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
test("discover registry via
|
|
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:
|
|
244
|
-
|
|
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:
|
|
264
|
-
|
|
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:
|
|
279
|
-
|
|
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:
|
|
291
|
-
|
|
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:
|
|
307
|
-
|
|
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:
|
|
327
|
-
|
|
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";
|
package/src/key-manager.ts
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
* - Signs JWTs with the active key
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import {
|
|
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
|
|
246
|
+
let builder = new SignJWT({ ...claims } as JWTPayload)
|
|
240
247
|
.setProtectedHeader({ alg: ALG, kid: key.kid })
|
|
241
248
|
.setIssuer(issuer)
|
|
242
249
|
.setIssuedAt();
|
package/src/registry-consumer.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Registry Consumer — Connects to registries and resolves refs.
|
|
3
3
|
*
|
|
4
|
-
* The consumer reads a `ConsumerConfig`,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
/**
|
|
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
|
|
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
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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
|
|
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
|
|
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
|
|
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,
|