@rickydata/agent0-mcp 0.1.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 (70) hide show
  1. package/.env.example +17 -0
  2. package/Dockerfile +25 -0
  3. package/README.md +85 -0
  4. package/dist/auth/sdk-client.d.ts +47 -0
  5. package/dist/auth/sdk-client.js +142 -0
  6. package/dist/auth/sdk-client.js.map +1 -0
  7. package/dist/auth/token.d.ts +5 -0
  8. package/dist/auth/token.js +6 -0
  9. package/dist/auth/token.js.map +1 -0
  10. package/dist/auth/wallet-derivation.d.ts +26 -0
  11. package/dist/auth/wallet-derivation.js +76 -0
  12. package/dist/auth/wallet-derivation.js.map +1 -0
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +140 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/tools/a2a.d.ts +3 -0
  17. package/dist/tools/a2a.js +170 -0
  18. package/dist/tools/a2a.js.map +1 -0
  19. package/dist/tools/discovery.d.ts +3 -0
  20. package/dist/tools/discovery.js +465 -0
  21. package/dist/tools/discovery.js.map +1 -0
  22. package/dist/tools/index.d.ts +3 -0
  23. package/dist/tools/index.js +38 -0
  24. package/dist/tools/index.js.map +1 -0
  25. package/dist/tools/payments.d.ts +3 -0
  26. package/dist/tools/payments.js +124 -0
  27. package/dist/tools/payments.js.map +1 -0
  28. package/dist/tools/registration.d.ts +3 -0
  29. package/dist/tools/registration.js +324 -0
  30. package/dist/tools/registration.js.map +1 -0
  31. package/dist/tools/reputation.d.ts +3 -0
  32. package/dist/tools/reputation.js +147 -0
  33. package/dist/tools/reputation.js.map +1 -0
  34. package/dist/utils/chains.d.ts +10 -0
  35. package/dist/utils/chains.js +33 -0
  36. package/dist/utils/chains.js.map +1 -0
  37. package/dist/utils/trust-labels.d.ts +10 -0
  38. package/dist/utils/trust-labels.js +48 -0
  39. package/dist/utils/trust-labels.js.map +1 -0
  40. package/dist/utils/validation.d.ts +12 -0
  41. package/dist/utils/validation.js +19 -0
  42. package/dist/utils/validation.js.map +1 -0
  43. package/package.json +32 -0
  44. package/src/auth/sdk-client.ts +171 -0
  45. package/src/auth/token.ts +19 -0
  46. package/src/auth/wallet-derivation.ts +91 -0
  47. package/src/index.ts +184 -0
  48. package/src/tools/a2a.ts +205 -0
  49. package/src/tools/discovery.ts +517 -0
  50. package/src/tools/index.ts +45 -0
  51. package/src/tools/payments.ts +146 -0
  52. package/src/tools/registration.ts +389 -0
  53. package/src/tools/reputation.ts +183 -0
  54. package/src/utils/chains.ts +42 -0
  55. package/src/utils/trust-labels.ts +53 -0
  56. package/src/utils/validation.ts +20 -0
  57. package/tests/a2a.test.ts +234 -0
  58. package/tests/chains.test.ts +57 -0
  59. package/tests/discovery.test.ts +455 -0
  60. package/tests/e2e.test.ts +234 -0
  61. package/tests/payments.test.ts +148 -0
  62. package/tests/registration.test.ts +313 -0
  63. package/tests/reputation.test.ts +231 -0
  64. package/tests/sdk-client.test.ts +143 -0
  65. package/tests/tool-router.test.ts +28 -0
  66. package/tests/trust-labels.test.ts +229 -0
  67. package/tests/validation.test.ts +132 -0
  68. package/tests/wallet-derivation.test.ts +109 -0
  69. package/tsconfig.json +8 -0
  70. package/vitest.config.ts +8 -0
@@ -0,0 +1,455 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { discoveryTools, handleDiscoveryTool } from "../src/tools/discovery.js";
3
+
4
+ // Mock agent0-sdk so tests don't hit the real API
5
+ vi.mock("agent0-sdk", () => {
6
+ const mockSearchAgents = vi.fn();
7
+ const mockGetAgent = vi.fn();
8
+ const mockGetReputationSummary = vi.fn();
9
+ const mockSearchFeedback = vi.fn();
10
+
11
+ return {
12
+ SDK: vi.fn().mockImplementation(() => ({
13
+ searchAgents: mockSearchAgents,
14
+ getAgent: mockGetAgent,
15
+ getReputationSummary: mockGetReputationSummary,
16
+ searchFeedback: mockSearchFeedback,
17
+ })),
18
+ __mocks: {
19
+ mockSearchAgents,
20
+ mockGetAgent,
21
+ mockGetReputationSummary,
22
+ mockSearchFeedback,
23
+ },
24
+ };
25
+ });
26
+
27
+ // Access mock functions
28
+ async function getMocks() {
29
+ const mod = await import("agent0-sdk");
30
+ return (mod as unknown as { __mocks: Record<string, ReturnType<typeof vi.fn>> }).__mocks;
31
+ }
32
+
33
+ function makeAgent(overrides: Record<string, unknown> = {}) {
34
+ return {
35
+ agentId: "11155111:42",
36
+ chainId: 11155111,
37
+ name: "Test Agent",
38
+ description: "A test agent",
39
+ image: "https://example.com/img.png",
40
+ active: true,
41
+ x402support: false,
42
+ owners: ["0x1234567890abcdef1234567890abcdef12345678"],
43
+ walletAddress: "0x1234567890abcdef1234567890abcdef12345678",
44
+ mcp: null,
45
+ a2a: null,
46
+ web: null,
47
+ ens: null,
48
+ mcpTools: [],
49
+ a2aSkills: [],
50
+ oasfSkills: [],
51
+ oasfDomains: [],
52
+ supportedTrusts: [],
53
+ feedbackCount: 0,
54
+ averageValue: 0,
55
+ updatedAt: 1700000000,
56
+ ...overrides,
57
+ };
58
+ }
59
+
60
+ describe("discovery tools", () => {
61
+ beforeEach(() => {
62
+ vi.clearAllMocks();
63
+ });
64
+
65
+ // ===========================================================================
66
+ // Tool registration
67
+ // ===========================================================================
68
+ describe("tool registration", () => {
69
+ it("registers 6 discovery tools", () => {
70
+ expect(discoveryTools).toHaveLength(6);
71
+ });
72
+
73
+ const expectedTools = [
74
+ "search_agents",
75
+ "get_agent",
76
+ "get_supported_chains",
77
+ "get_platform_stats",
78
+ "get_reputation_summary",
79
+ "search_feedback",
80
+ ];
81
+
82
+ for (const toolName of expectedTools) {
83
+ it(`registers ${toolName}`, () => {
84
+ const tool = discoveryTools.find((t) => t.name === toolName);
85
+ expect(tool).toBeDefined();
86
+ expect(tool!.description).toBeTypeOf("string");
87
+ expect(tool!.description.length).toBeGreaterThan(10);
88
+ expect(tool!.inputSchema).toBeDefined();
89
+ });
90
+ }
91
+ });
92
+
93
+ // ===========================================================================
94
+ // search_agents
95
+ // ===========================================================================
96
+ describe("search_agents", () => {
97
+ it("returns formatted agents with trust labels", async () => {
98
+ const mocks = await getMocks();
99
+ mocks.mockSearchAgents.mockResolvedValue([
100
+ makeAgent({ feedbackCount: 25, averageValue: 90 }),
101
+ ]);
102
+
103
+ const result = (await handleDiscoveryTool("search_agents", {})) as {
104
+ count: number;
105
+ agents: Array<{ trust: string }>;
106
+ };
107
+
108
+ expect(result.count).toBe(1);
109
+ expect(result.agents[0].trust).toContain("Highly Trusted");
110
+ });
111
+
112
+ it("respects limit parameter", async () => {
113
+ const mocks = await getMocks();
114
+ const manyAgents = Array.from({ length: 50 }, (_, i) =>
115
+ makeAgent({ agentId: `11155111:${i}` }),
116
+ );
117
+ mocks.mockSearchAgents.mockResolvedValue(manyAgents);
118
+
119
+ const result = (await handleDiscoveryTool("search_agents", {
120
+ limit: 5,
121
+ })) as { count: number; totalAvailable: number };
122
+
123
+ expect(result.count).toBe(5);
124
+ expect(result.totalAvailable).toBe(50);
125
+ });
126
+
127
+ it("caps limit at 100", async () => {
128
+ const mocks = await getMocks();
129
+ const agents = Array.from({ length: 150 }, (_, i) =>
130
+ makeAgent({ agentId: `11155111:${i}` }),
131
+ );
132
+ mocks.mockSearchAgents.mockResolvedValue(agents);
133
+
134
+ const result = (await handleDiscoveryTool("search_agents", {
135
+ limit: 200,
136
+ })) as { count: number };
137
+
138
+ expect(result.count).toBe(100);
139
+ });
140
+
141
+ it("handles empty results", async () => {
142
+ const mocks = await getMocks();
143
+ mocks.mockSearchAgents.mockResolvedValue([]);
144
+
145
+ const result = (await handleDiscoveryTool("search_agents", {
146
+ name: "nonexistent",
147
+ })) as { count: number; agents: unknown[] };
148
+
149
+ expect(result.count).toBe(0);
150
+ expect(result.agents).toEqual([]);
151
+ });
152
+
153
+ it("passes filter parameters to SDK", async () => {
154
+ const mocks = await getMocks();
155
+ mocks.mockSearchAgents.mockResolvedValue([]);
156
+
157
+ await handleDiscoveryTool("search_agents", {
158
+ name: "defi",
159
+ active: true,
160
+ x402support: true,
161
+ hasMCP: true,
162
+ });
163
+
164
+ expect(mocks.mockSearchAgents).toHaveBeenCalledWith(
165
+ expect.objectContaining({
166
+ name: "defi",
167
+ active: true,
168
+ x402support: true,
169
+ hasMCP: true,
170
+ }),
171
+ expect.any(Object),
172
+ );
173
+ });
174
+ });
175
+
176
+ // ===========================================================================
177
+ // get_agent
178
+ // ===========================================================================
179
+ describe("get_agent", () => {
180
+ it("returns full agent profile with reputation", async () => {
181
+ const mocks = await getMocks();
182
+ mocks.mockGetAgent.mockResolvedValue(
183
+ makeAgent({ name: "DeFi Bot", feedbackCount: 10, averageValue: 75 }),
184
+ );
185
+ mocks.mockGetReputationSummary.mockResolvedValue({
186
+ count: 10,
187
+ averageValue: 75,
188
+ });
189
+
190
+ const result = (await handleDiscoveryTool("get_agent", {
191
+ agentId: "11155111:42",
192
+ })) as { name: string; reputation: { trustLabel: string }; chain: string };
193
+
194
+ expect(result.name).toBe("DeFi Bot");
195
+ expect(result.reputation.trustLabel).toBe("Trusted");
196
+ expect(result.chain).toContain("Chain"); // getChainName for 11155111 returns fallback
197
+ });
198
+
199
+ it("returns error for missing agentId", async () => {
200
+ const result = (await handleDiscoveryTool("get_agent", {})) as {
201
+ error: string;
202
+ };
203
+ expect(result.error).toContain("agentId is required");
204
+ });
205
+
206
+ it("returns error for invalid agentId format", async () => {
207
+ const result = (await handleDiscoveryTool("get_agent", {
208
+ agentId: "invalid",
209
+ })) as { error: string };
210
+ expect(result.error).toContain("Invalid agentId format");
211
+ });
212
+
213
+ it("returns error when agent not found", async () => {
214
+ const mocks = await getMocks();
215
+ mocks.mockGetAgent.mockResolvedValue(null);
216
+
217
+ const result = (await handleDiscoveryTool("get_agent", {
218
+ agentId: "11155111:99999",
219
+ })) as { error: string };
220
+ expect(result.error).toContain("not found");
221
+ });
222
+
223
+ it("handles reputation fetch failure gracefully", async () => {
224
+ const mocks = await getMocks();
225
+ mocks.mockGetAgent.mockResolvedValue(makeAgent());
226
+ mocks.mockGetReputationSummary.mockRejectedValue(
227
+ new Error("Reputation unavailable"),
228
+ );
229
+
230
+ const result = (await handleDiscoveryTool("get_agent", {
231
+ agentId: "11155111:42",
232
+ })) as { reputation: { count: number; averageValue: number } };
233
+
234
+ // Should still return agent with default reputation
235
+ expect(result.reputation.count).toBe(0);
236
+ expect(result.reputation.averageValue).toBe(0);
237
+ });
238
+ });
239
+
240
+ // ===========================================================================
241
+ // get_supported_chains
242
+ // ===========================================================================
243
+ describe("get_supported_chains", () => {
244
+ it("returns chain list with count", async () => {
245
+ const result = (await handleDiscoveryTool(
246
+ "get_supported_chains",
247
+ {},
248
+ )) as { count: number; chains: Array<{ chainId: number; name: string }> };
249
+
250
+ expect(result.count).toBeGreaterThan(0);
251
+ expect(result.chains.length).toBe(result.count);
252
+ });
253
+
254
+ it("includes Ethereum Mainnet and Base", async () => {
255
+ const result = (await handleDiscoveryTool(
256
+ "get_supported_chains",
257
+ {},
258
+ )) as { chains: Array<{ chainId: number }> };
259
+
260
+ const chainIds = result.chains.map((c) => c.chainId);
261
+ expect(chainIds).toContain(1);
262
+ expect(chainIds).toContain(8453);
263
+ });
264
+
265
+ it("each chain has required fields", async () => {
266
+ const result = (await handleDiscoveryTool(
267
+ "get_supported_chains",
268
+ {},
269
+ )) as {
270
+ chains: Array<{
271
+ chainId: number;
272
+ name: string;
273
+ identity: string;
274
+ reputation: string;
275
+ hasSubgraph: boolean;
276
+ }>;
277
+ };
278
+
279
+ for (const chain of result.chains) {
280
+ expect(chain.chainId).toBeTypeOf("number");
281
+ expect(chain.name).toBeTypeOf("string");
282
+ expect(chain.identity).toMatch(/^0x/);
283
+ expect(chain.reputation).toMatch(/^0x/);
284
+ expect(chain.hasSubgraph).toBeTypeOf("boolean");
285
+ }
286
+ });
287
+ });
288
+
289
+ // ===========================================================================
290
+ // get_platform_stats
291
+ // ===========================================================================
292
+ describe("get_platform_stats", () => {
293
+ it("returns aggregate stats", async () => {
294
+ const mocks = await getMocks();
295
+ mocks.mockSearchAgents.mockResolvedValue([
296
+ makeAgent({ active: true, mcp: "https://mcp.test", x402support: true }),
297
+ makeAgent({ active: false, a2a: "https://a2a.test", feedbackCount: 5 }),
298
+ makeAgent({ active: true }),
299
+ ]);
300
+
301
+ const result = (await handleDiscoveryTool("get_platform_stats", {})) as {
302
+ totalAgents: number;
303
+ activeAgents: number;
304
+ agentsWithMCP: number;
305
+ agentsWithA2A: number;
306
+ agentsWithX402: number;
307
+ agentsWithFeedback: number;
308
+ };
309
+
310
+ expect(result.totalAgents).toBe(3);
311
+ expect(result.activeAgents).toBe(2);
312
+ expect(result.agentsWithMCP).toBe(1);
313
+ expect(result.agentsWithA2A).toBe(1);
314
+ expect(result.agentsWithX402).toBe(1);
315
+ expect(result.agentsWithFeedback).toBe(1);
316
+ });
317
+
318
+ it("uses provided chainId", async () => {
319
+ const mocks = await getMocks();
320
+ mocks.mockSearchAgents.mockResolvedValue([]);
321
+ const { SDK } = await import("agent0-sdk");
322
+
323
+ await handleDiscoveryTool("get_platform_stats", { chainId: 8453 });
324
+
325
+ expect(SDK).toHaveBeenCalledWith(
326
+ expect.objectContaining({ chainId: 8453 }),
327
+ );
328
+ });
329
+ });
330
+
331
+ // ===========================================================================
332
+ // get_reputation_summary
333
+ // ===========================================================================
334
+ describe("get_reputation_summary", () => {
335
+ it("returns reputation with trust label", async () => {
336
+ const mocks = await getMocks();
337
+ mocks.mockGetReputationSummary.mockResolvedValue({
338
+ count: 15,
339
+ averageValue: 72,
340
+ });
341
+
342
+ const result = (await handleDiscoveryTool("get_reputation_summary", {
343
+ agentId: "11155111:42",
344
+ })) as { trustLabel: string; count: number; averageValue: number };
345
+
346
+ expect(result.count).toBe(15);
347
+ expect(result.averageValue).toBe(72);
348
+ expect(result.trustLabel).toBe("Trusted");
349
+ });
350
+
351
+ it("returns error for missing agentId", async () => {
352
+ const result = (await handleDiscoveryTool(
353
+ "get_reputation_summary",
354
+ {},
355
+ )) as { error: string };
356
+ expect(result.error).toContain("agentId is required");
357
+ });
358
+
359
+ it("returns error for invalid format", async () => {
360
+ const result = (await handleDiscoveryTool("get_reputation_summary", {
361
+ agentId: "bad-format",
362
+ })) as { error: string };
363
+ expect(result.error).toContain("Invalid agentId format");
364
+ });
365
+
366
+ it("passes tag to SDK", async () => {
367
+ const mocks = await getMocks();
368
+ mocks.mockGetReputationSummary.mockResolvedValue({
369
+ count: 0,
370
+ averageValue: 0,
371
+ });
372
+
373
+ await handleDiscoveryTool("get_reputation_summary", {
374
+ agentId: "11155111:42",
375
+ tag: "quality",
376
+ });
377
+
378
+ expect(mocks.mockGetReputationSummary).toHaveBeenCalledWith(
379
+ "11155111:42",
380
+ "quality",
381
+ );
382
+ });
383
+ });
384
+
385
+ // ===========================================================================
386
+ // search_feedback
387
+ // ===========================================================================
388
+ describe("search_feedback", () => {
389
+ it("returns formatted feedback entries", async () => {
390
+ const mocks = await getMocks();
391
+ mocks.mockSearchFeedback.mockResolvedValue([
392
+ {
393
+ agentId: "11155111:42",
394
+ reviewer: "0xabc",
395
+ value: 85,
396
+ tags: ["quality"],
397
+ text: "Great agent",
398
+ endpoint: "mcp",
399
+ isRevoked: false,
400
+ createdAt: 1700000000,
401
+ mcpTool: "search",
402
+ a2aSkills: [],
403
+ },
404
+ ]);
405
+
406
+ const result = (await handleDiscoveryTool("search_feedback", {
407
+ agentId: "11155111:42",
408
+ })) as { count: number; feedbacks: Array<{ value: number }> };
409
+
410
+ expect(result.count).toBe(1);
411
+ expect(result.feedbacks[0].value).toBe(85);
412
+ });
413
+
414
+ it("returns error when no agent specified", async () => {
415
+ const result = (await handleDiscoveryTool("search_feedback", {})) as {
416
+ error: string;
417
+ };
418
+ expect(result.error).toContain("Provide agentId or agents[]");
419
+ });
420
+
421
+ it("caps results at 50", async () => {
422
+ const mocks = await getMocks();
423
+ const manyFeedbacks = Array.from({ length: 60 }, (_, i) => ({
424
+ agentId: "11155111:42",
425
+ reviewer: `0x${i}`,
426
+ value: 50,
427
+ tags: [],
428
+ text: "",
429
+ endpoint: null,
430
+ isRevoked: false,
431
+ createdAt: 1700000000,
432
+ mcpTool: null,
433
+ a2aSkills: [],
434
+ }));
435
+ mocks.mockSearchFeedback.mockResolvedValue(manyFeedbacks);
436
+
437
+ const result = (await handleDiscoveryTool("search_feedback", {
438
+ agentId: "11155111:42",
439
+ })) as { count: number; feedbacks: unknown[] };
440
+
441
+ expect(result.count).toBe(60);
442
+ expect(result.feedbacks).toHaveLength(50);
443
+ });
444
+ });
445
+
446
+ // ===========================================================================
447
+ // Unknown tool
448
+ // ===========================================================================
449
+ it("returns error for unknown tool name", async () => {
450
+ const result = (await handleDiscoveryTool("nonexistent", {})) as {
451
+ error: string;
452
+ };
453
+ expect(result.error).toContain("Unknown discovery tool");
454
+ });
455
+ });
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Integration tests against live 8004scan / agent0-sdk API.
3
+ *
4
+ * Run with: LIVE_TESTS=1 npx vitest run tests/e2e.test.ts
5
+ *
6
+ * These tests call the real agent0-sdk which hits the 8004scan subgraph.
7
+ * They are slow (~5-15s each) and require network access.
8
+ * Set EIGHTSCAN_API_KEY to avoid rate limits.
9
+ */
10
+ import { describe, it, expect } from "vitest";
11
+ import { handleDiscoveryTool } from "../src/tools/discovery.js";
12
+
13
+ const LIVE = !!process.env.LIVE_TESTS;
14
+
15
+ describe.skipIf(!LIVE)("e2e: live agent0-sdk via discovery tools", () => {
16
+ it(
17
+ "search_agents: returns agents on default chain (Sepolia)",
18
+ async () => {
19
+ const result = (await handleDiscoveryTool("search_agents", {
20
+ limit: 5,
21
+ })) as {
22
+ count: number;
23
+ totalAvailable: number;
24
+ chainId: number;
25
+ agents: Array<{
26
+ agentId: string;
27
+ name: string;
28
+ trust: string;
29
+ }>;
30
+ };
31
+
32
+ expect(result.count).toBeGreaterThan(0);
33
+ expect(result.count).toBeLessThanOrEqual(5);
34
+ expect(result.totalAvailable).toBeGreaterThan(0);
35
+
36
+ // Each agent should have required fields
37
+ for (const agent of result.agents) {
38
+ expect(agent.agentId).toBeTypeOf("string");
39
+ expect(agent.agentId).toContain(":");
40
+ expect(agent.name).toBeTypeOf("string");
41
+ expect(agent.trust).toBeTypeOf("string");
42
+ }
43
+ },
44
+ 30_000,
45
+ );
46
+
47
+ it(
48
+ "search_agents: name filter returns matching results",
49
+ async () => {
50
+ const result = (await handleDiscoveryTool("search_agents", {
51
+ name: "test",
52
+ limit: 10,
53
+ })) as {
54
+ count: number;
55
+ agents: Array<{ name: string }>;
56
+ };
57
+
58
+ // May return 0 if no agents match, but should not error
59
+ expect(result.count).toBeGreaterThanOrEqual(0);
60
+ expect(Array.isArray(result.agents)).toBe(true);
61
+ },
62
+ 30_000,
63
+ );
64
+
65
+ it(
66
+ "get_agent: returns a known agent profile",
67
+ async () => {
68
+ // First search to find a real agent ID
69
+ const searchResult = (await handleDiscoveryTool("search_agents", {
70
+ limit: 1,
71
+ })) as {
72
+ agents: Array<{ agentId: string }>;
73
+ };
74
+
75
+ expect(searchResult.agents.length).toBeGreaterThan(0);
76
+ const agentId = searchResult.agents[0].agentId;
77
+
78
+ const result = (await handleDiscoveryTool("get_agent", {
79
+ agentId,
80
+ })) as {
81
+ agentId: string;
82
+ name: string;
83
+ reputation: {
84
+ count: number;
85
+ averageValue: number;
86
+ trustLabel: string;
87
+ trustDisplay: string;
88
+ };
89
+ chain: string;
90
+ };
91
+
92
+ expect(result.agentId).toBe(agentId);
93
+ expect(result.name).toBeTypeOf("string");
94
+ expect(result.reputation).toBeDefined();
95
+ expect(result.reputation.trustLabel).toBeTypeOf("string");
96
+ expect(result.chain).toBeTypeOf("string");
97
+ },
98
+ 30_000,
99
+ );
100
+
101
+ it(
102
+ "get_agent: returns error for nonexistent agent",
103
+ async () => {
104
+ const result = (await handleDiscoveryTool("get_agent", {
105
+ agentId: "11155111:999999999",
106
+ })) as { error?: string };
107
+
108
+ expect(result.error).toBeDefined();
109
+ expect(result.error).toContain("not found");
110
+ },
111
+ 30_000,
112
+ );
113
+
114
+ it(
115
+ "get_supported_chains: returns chain list with contract addresses",
116
+ async () => {
117
+ const result = (await handleDiscoveryTool(
118
+ "get_supported_chains",
119
+ {},
120
+ )) as {
121
+ count: number;
122
+ chains: Array<{
123
+ chainId: number;
124
+ name: string;
125
+ identity: string;
126
+ reputation: string;
127
+ hasSubgraph: boolean;
128
+ }>;
129
+ note: string;
130
+ };
131
+
132
+ expect(result.count).toBeGreaterThanOrEqual(3);
133
+ expect(result.chains.length).toBe(result.count);
134
+
135
+ // Verify known chains are present
136
+ const chainIds = result.chains.map((c) => c.chainId);
137
+ expect(chainIds).toContain(1); // Ethereum
138
+ expect(chainIds).toContain(8453); // Base
139
+ expect(chainIds).toContain(11155111); // Sepolia
140
+
141
+ // Each chain has contract addresses
142
+ for (const chain of result.chains) {
143
+ expect(chain.identity).toMatch(/^0x[0-9a-fA-F]{40}$/);
144
+ expect(chain.reputation).toMatch(/^0x[0-9a-fA-F]{40}$/);
145
+ }
146
+ },
147
+ 15_000,
148
+ );
149
+
150
+ it(
151
+ "get_platform_stats: returns aggregate stats for Sepolia",
152
+ async () => {
153
+ const result = (await handleDiscoveryTool("get_platform_stats", {
154
+ chainId: 11155111,
155
+ })) as {
156
+ chainId: number;
157
+ chain: string;
158
+ totalAgents: number;
159
+ activeAgents: number;
160
+ agentsWithMCP: number;
161
+ agentsWithA2A: number;
162
+ agentsWithX402: number;
163
+ totalMCPTools: number;
164
+ };
165
+
166
+ expect(result.chainId).toBe(11155111);
167
+ expect(result.totalAgents).toBeGreaterThan(0);
168
+ expect(result.activeAgents).toBeGreaterThanOrEqual(0);
169
+ expect(result.activeAgents).toBeLessThanOrEqual(result.totalAgents);
170
+ },
171
+ 30_000,
172
+ );
173
+
174
+ it(
175
+ "get_reputation_summary: returns trust label for an agent",
176
+ async () => {
177
+ // Find a real agent first
178
+ const searchResult = (await handleDiscoveryTool("search_agents", {
179
+ limit: 1,
180
+ })) as { agents: Array<{ agentId: string }> };
181
+ const agentId = searchResult.agents[0].agentId;
182
+
183
+ const result = (await handleDiscoveryTool("get_reputation_summary", {
184
+ agentId,
185
+ })) as {
186
+ agentId: string;
187
+ count: number;
188
+ averageValue: number;
189
+ trustLabel: string;
190
+ trustEmoji: string;
191
+ trustDisplay: string;
192
+ };
193
+
194
+ expect(result.agentId).toBe(agentId);
195
+ expect(result.count).toBeGreaterThanOrEqual(0);
196
+ expect(result.trustLabel).toBeTypeOf("string");
197
+ expect(result.trustEmoji).toBeTypeOf("string");
198
+ },
199
+ 30_000,
200
+ );
201
+
202
+ it(
203
+ "search_feedback: returns feedback entries (may be empty)",
204
+ async () => {
205
+ // Find a real agent first
206
+ const searchResult = (await handleDiscoveryTool("search_agents", {
207
+ limit: 1,
208
+ })) as { agents: Array<{ agentId: string }> };
209
+ const agentId = searchResult.agents[0].agentId;
210
+
211
+ const result = (await handleDiscoveryTool("search_feedback", {
212
+ agentId,
213
+ })) as {
214
+ count: number;
215
+ feedbacks: Array<{
216
+ agentId: string;
217
+ reviewer: string;
218
+ value: number;
219
+ }>;
220
+ };
221
+
222
+ expect(result.count).toBeGreaterThanOrEqual(0);
223
+ expect(Array.isArray(result.feedbacks)).toBe(true);
224
+
225
+ // If any feedback exists, verify shape
226
+ if (result.feedbacks.length > 0) {
227
+ const fb = result.feedbacks[0];
228
+ expect(fb.agentId).toBeTypeOf("string");
229
+ expect(fb.value).toBeTypeOf("number");
230
+ }
231
+ },
232
+ 30_000,
233
+ );
234
+ });