kompass-sdk 0.20.0 → 0.21.1

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 (84) hide show
  1. package/dist/aggregator.d.ts.map +1 -1
  2. package/dist/aggregator.js +13 -6
  3. package/dist/aggregator.js.map +1 -1
  4. package/dist/basename.d.ts +59 -0
  5. package/dist/basename.d.ts.map +1 -0
  6. package/dist/basename.js +253 -0
  7. package/dist/basename.js.map +1 -0
  8. package/dist/bridge.d.ts.map +1 -1
  9. package/dist/bridge.js +64 -0
  10. package/dist/bridge.js.map +1 -1
  11. package/dist/cli.js +101 -0
  12. package/dist/cli.js.map +1 -1
  13. package/dist/erc8004.d.ts +179 -0
  14. package/dist/erc8004.d.ts.map +1 -0
  15. package/dist/erc8004.js +187 -0
  16. package/dist/erc8004.js.map +1 -0
  17. package/dist/index.d.ts +6 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +6 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/matching.d.ts.map +1 -1
  22. package/dist/matching.js +7 -6
  23. package/dist/matching.js.map +1 -1
  24. package/dist/onboard.d.ts +132 -0
  25. package/dist/onboard.d.ts.map +1 -0
  26. package/dist/onboard.js +285 -0
  27. package/dist/onboard.js.map +1 -0
  28. package/dist/router.js +2 -2
  29. package/dist/router.js.map +1 -1
  30. package/dist/sources/acp.d.ts.map +1 -1
  31. package/dist/sources/acp.js +8 -7
  32. package/dist/sources/acp.js.map +1 -1
  33. package/dist/sources/bankr.d.ts.map +1 -1
  34. package/dist/sources/bankr.js +7 -4
  35. package/dist/sources/bankr.js.map +1 -1
  36. package/dist/sources/erc8004.js +2 -2
  37. package/dist/sources/erc8004.js.map +1 -1
  38. package/dist/sources/locus.d.ts.map +1 -1
  39. package/dist/sources/locus.js +4 -0
  40. package/dist/sources/locus.js.map +1 -1
  41. package/dist/sources/mcp-registry.d.ts.map +1 -1
  42. package/dist/sources/mcp-registry.js +6 -0
  43. package/dist/sources/mcp-registry.js.map +1 -1
  44. package/dist/sources/olas.d.ts +3 -3
  45. package/dist/sources/olas.d.ts.map +1 -1
  46. package/dist/sources/olas.js +37 -31
  47. package/dist/sources/olas.js.map +1 -1
  48. package/dist/sources/skills.d.ts.map +1 -1
  49. package/dist/sources/skills.js +4 -2
  50. package/dist/sources/skills.js.map +1 -1
  51. package/dist/sources/x402-ecosystem.js +1 -1
  52. package/dist/sources/x402-ecosystem.js.map +1 -1
  53. package/dist/wallet/handlers/bankr.d.ts.map +1 -1
  54. package/dist/wallet/handlers/bankr.js +5 -4
  55. package/dist/wallet/handlers/bankr.js.map +1 -1
  56. package/dist/wallet/handlers/locus.d.ts.map +1 -1
  57. package/dist/wallet/handlers/locus.js +3 -2
  58. package/dist/wallet/handlers/locus.js.map +1 -1
  59. package/dist/wallet/handlers/olas.d.ts +5 -1
  60. package/dist/wallet/handlers/olas.d.ts.map +1 -1
  61. package/dist/wallet/handlers/olas.js +20 -28
  62. package/dist/wallet/handlers/olas.js.map +1 -1
  63. package/dist/wallet/index.d.ts +20 -20
  64. package/package.json +1 -1
  65. package/src/aggregator.ts +8 -4
  66. package/src/basename.ts +334 -0
  67. package/src/bridge.ts +53 -0
  68. package/src/cli.ts +113 -0
  69. package/src/erc8004.ts +253 -0
  70. package/src/index.ts +12 -0
  71. package/src/matching.ts +7 -6
  72. package/src/onboard.ts +420 -0
  73. package/src/router.ts +2 -2
  74. package/src/sources/acp.ts +8 -7
  75. package/src/sources/bankr.ts +6 -4
  76. package/src/sources/erc8004.ts +2 -2
  77. package/src/sources/locus.ts +5 -0
  78. package/src/sources/mcp-registry.ts +6 -0
  79. package/src/sources/olas.ts +53 -44
  80. package/src/sources/skills.ts +5 -2
  81. package/src/sources/x402-ecosystem.ts +1 -1
  82. package/src/wallet/handlers/bankr.ts +6 -4
  83. package/src/wallet/handlers/locus.ts +3 -2
  84. package/src/wallet/handlers/olas.ts +25 -30
package/src/onboard.ts ADDED
@@ -0,0 +1,420 @@
1
+ /**
2
+ * Kompass Agent Onboarding — Full Identity Registration
3
+ *
4
+ * Orchestrates the complete flow:
5
+ * 1. Create wallet (free — generates keypair)
6
+ * 2. Register ERC-8004 identity (~$0.005 gas)
7
+ * 3. Register Basename (~$0.25-$2.50)
8
+ * 4. Set ENS text records (~$0.02 gas)
9
+ * 5. Register on Kompass Registry (gas only)
10
+ *
11
+ * Total cost: ~$2.55 in ETH (agent pays from their own wallet)
12
+ */
13
+
14
+ import type { Address, Hex } from "viem";
15
+ import { KompassWallet } from "./wallet/index.js";
16
+ import {
17
+ registerERC8004,
18
+ buildAgentCard,
19
+ ERC8004_IDENTITY_REGISTRY,
20
+ type RegisterERC8004Result,
21
+ } from "./erc8004.js";
22
+ import {
23
+ registerBasename,
24
+ setBasenameTextRecords,
25
+ basenameNode,
26
+ isBasenameAvailable,
27
+ getBasenamePrice,
28
+ BASENAME_REGISTRAR,
29
+ BASENAME_L2_RESOLVER,
30
+ type BasenameResult,
31
+ type TextRecord,
32
+ } from "./basename.js";
33
+
34
+ // ── Types ──────────────────────────────────────────────────
35
+
36
+ export interface OnboardOptions {
37
+ /** Agent name (used for Basename, e.g. "my-agent" → my-agent.base.eth) */
38
+ name: string;
39
+ /** What this agent does */
40
+ description: string;
41
+ /** e.g. ["defi", "trading", "data"] */
42
+ categories: string[];
43
+ /** What the agent can do */
44
+ capabilities: string[];
45
+ /** Agent role */
46
+ agentType: "provider" | "evaluator" | "both";
47
+ /** Reachable endpoints */
48
+ endpoints?: {
49
+ mcp?: string;
50
+ a2a?: string;
51
+ x402?: string;
52
+ http?: string;
53
+ };
54
+ /** How this agent gets paid */
55
+ pricingModel?: "escrow" | "x402" | "free";
56
+ /** e.g. "0.01" (USDC per task) */
57
+ pricingRate?: string;
58
+ /** Network to deploy on */
59
+ network?: "base" | "base-sepolia";
60
+ /** Skip Basename registration if agent already has a name */
61
+ skipBasename?: boolean;
62
+ /** Skip Kompass Registry (e.g. if on mainnet where registry isn't deployed) */
63
+ skipKompassRegistry?: boolean;
64
+ /** Existing wallet path (skip wallet creation) */
65
+ walletPath?: string;
66
+ /** Wallet password */
67
+ walletPassword?: string;
68
+ /** URL where the agent card JSON will be hosted */
69
+ agentCardUrl?: string;
70
+ }
71
+
72
+ export interface OnboardResult {
73
+ wallet: {
74
+ address: Address;
75
+ network: string;
76
+ keystorePath: string;
77
+ isNew: boolean;
78
+ };
79
+ erc8004: RegisterERC8004Result;
80
+ basename?: BasenameResult;
81
+ ensRecords: {
82
+ txHashes: Hex[];
83
+ records: Record<string, string>;
84
+ };
85
+ kompassRegistry?: {
86
+ txHash: Hex;
87
+ ensNode: Hex;
88
+ };
89
+ agentCard: {
90
+ name: string;
91
+ description: string;
92
+ capabilities: string[];
93
+ categories: string[];
94
+ paymentAddress: Address;
95
+ };
96
+ totalSteps: number;
97
+ completedSteps: number;
98
+ }
99
+
100
+ export interface OnboardInitResult {
101
+ walletAddress: Address;
102
+ network: string;
103
+ keystorePath: string;
104
+ fundingRequired: {
105
+ eth: string;
106
+ description: string;
107
+ };
108
+ estimatedCosts: {
109
+ erc8004Gas: string;
110
+ basename: string;
111
+ ensRecordsGas: string;
112
+ total: string;
113
+ };
114
+ }
115
+
116
+ // ── Step 1: Initialize (create wallet, return funding instructions) ────
117
+
118
+ /**
119
+ * Initialize onboarding: create wallet, estimate costs.
120
+ * Call this first, then fund the wallet, then call completeOnboarding().
121
+ */
122
+ export function initOnboarding(options: OnboardOptions): OnboardInitResult {
123
+ const network = options.network ?? "base";
124
+
125
+ // Create or load wallet
126
+ let wallet: KompassWallet;
127
+ if (options.walletPath) {
128
+ wallet = KompassWallet.load(options.walletPath, options.walletPassword);
129
+ } else {
130
+ wallet = KompassWallet.create({ network, password: options.walletPassword });
131
+ }
132
+
133
+ const basenameEstimate = options.skipBasename
134
+ ? "0"
135
+ : options.name.length >= 10
136
+ ? "0.0001"
137
+ : "0.001";
138
+
139
+ return {
140
+ walletAddress: wallet.getAddress(),
141
+ network,
142
+ keystorePath: options.walletPath ?? `${process.env.HOME ?? "~"}/.kompass/wallet.json`,
143
+ fundingRequired: {
144
+ eth: options.skipBasename ? "0.001" : "0.003",
145
+ description: `Send ETH to ${wallet.getAddress()} on Base ${network === "base" ? "mainnet" : "Sepolia"} to cover gas + basename registration`,
146
+ },
147
+ estimatedCosts: {
148
+ erc8004Gas: "~0.0001 ETH",
149
+ basename: options.skipBasename ? "skipped" : `~${basenameEstimate} ETH`,
150
+ ensRecordsGas: "~0.0005 ETH",
151
+ total: options.skipBasename ? "~0.001 ETH" : `~${(parseFloat(basenameEstimate) + 0.001).toFixed(4)} ETH`,
152
+ },
153
+ };
154
+ }
155
+
156
+ // ── Step 2: Complete (run full registration after funding) ─────────────
157
+
158
+ /**
159
+ * Complete the full onboarding flow. The wallet must be funded before calling this.
160
+ *
161
+ * Flow:
162
+ * 1. Build agent card JSON
163
+ * 2. Register ERC-8004 identity (mint agent NFT)
164
+ * 3. Register Basename (optional)
165
+ * 4. Set ENS text records (agent metadata)
166
+ * 5. Register on Kompass Registry (optional)
167
+ */
168
+ export async function completeOnboarding(
169
+ options: OnboardOptions,
170
+ walletPathOrWallet?: string | KompassWallet,
171
+ ): Promise<OnboardResult> {
172
+ const network = options.network ?? "base";
173
+
174
+ // Load or create wallet
175
+ let wallet: KompassWallet;
176
+ let isNew = false;
177
+ if (walletPathOrWallet instanceof KompassWallet) {
178
+ wallet = walletPathOrWallet;
179
+ } else {
180
+ const loadPath = walletPathOrWallet ?? options.walletPath;
181
+ try {
182
+ wallet = KompassWallet.load(loadPath, options.walletPassword);
183
+ } catch {
184
+ // Wallet doesn't exist yet — create it
185
+ wallet = KompassWallet.create({ path: loadPath, network, password: options.walletPassword });
186
+ isNew = true;
187
+ }
188
+ }
189
+
190
+ const address = wallet.getAddress();
191
+ const totalSteps = 2 + (options.skipBasename ? 0 : 1) + 1 + (options.skipKompassRegistry ? 0 : 1);
192
+ let completedSteps = 0;
193
+
194
+ // Check balance
195
+ const balance = await wallet.getBalance();
196
+ if (balance.ethRaw < 500000000000000n) {
197
+ // < 0.0005 ETH
198
+ throw new Error(
199
+ `Insufficient ETH balance: ${balance.eth} ETH. ` +
200
+ `Fund wallet ${address} with at least 0.003 ETH on Base ${network === "base" ? "mainnet" : "Sepolia"}.`,
201
+ );
202
+ }
203
+
204
+ // ── Step 1: Build agent card ──────────────────────────────
205
+ const agentCard = buildAgentCard({
206
+ name: options.name,
207
+ description: options.description,
208
+ capabilities: options.capabilities,
209
+ categories: options.categories,
210
+ walletAddress: address,
211
+ endpoints: options.endpoints,
212
+ });
213
+
214
+ // Determine agent card URL
215
+ const agentCardUrl = options.agentCardUrl ?? `https://kompasss.xyz/agents/${address}.json`;
216
+ completedSteps++;
217
+
218
+ // ── Step 2: Register ERC-8004 Identity ────────────────────
219
+ console.log(`[onboard] Registering ERC-8004 identity for ${options.name}...`);
220
+ const erc8004Result = await registerERC8004(wallet, agentCardUrl);
221
+ console.log(`[onboard] ERC-8004 identity registered: agentId=${erc8004Result.agentId}`);
222
+ completedSteps++;
223
+
224
+ // ── Step 3: Register Basename (optional) ──────────────────
225
+ let basenameResult: BasenameResult | undefined;
226
+ if (!options.skipBasename) {
227
+ console.log(`[onboard] Registering basename: ${options.name}.base.eth...`);
228
+
229
+ // Build text records to set atomically during registration
230
+ const textRecords: TextRecord[] = [
231
+ { key: "com.kompass.type", value: options.agentType },
232
+ { key: "com.kompass.categories", value: options.categories.join(",") },
233
+ { key: "com.kompass.description", value: options.description },
234
+ { key: "com.kompass.erc8004.id", value: erc8004Result.agentId.toString() },
235
+ ];
236
+
237
+ if (options.endpoints?.mcp) textRecords.push({ key: "com.kompass.endpoint.mcp", value: options.endpoints.mcp });
238
+ if (options.endpoints?.a2a) textRecords.push({ key: "com.kompass.endpoint.a2a", value: options.endpoints.a2a });
239
+ if (options.endpoints?.x402) textRecords.push({ key: "com.kompass.endpoint.x402", value: options.endpoints.x402 });
240
+ if (options.endpoints?.http) textRecords.push({ key: "com.kompass.endpoint.http", value: options.endpoints.http });
241
+ if (options.pricingModel) textRecords.push({ key: "com.kompass.payment.mode", value: options.pricingModel });
242
+ if (options.pricingRate) textRecords.push({ key: "com.kompass.pricing.rate", value: options.pricingRate });
243
+
244
+ try {
245
+ basenameResult = await registerBasename(wallet, options.name, textRecords);
246
+ console.log(`[onboard] Basename registered: ${basenameResult.fullName}`);
247
+ } catch (err: any) {
248
+ console.warn(`[onboard] Basename registration failed (non-critical): ${err.message}`);
249
+ }
250
+ completedSteps++;
251
+ }
252
+
253
+ // ── Step 4: Set ENS text records (if basename was skipped or failed) ──
254
+ const ensRecords: Record<string, string> = {};
255
+ const ensRecordTxHashes: Hex[] = [];
256
+
257
+ // If basename was registered with text records, they're already set atomically.
258
+ // If basename failed or was skipped but we have an existing name, set records separately.
259
+ if (!basenameResult && basenameResult === undefined) {
260
+ // Try setting records on the name if it exists (maybe registered before)
261
+ try {
262
+ const { setBasenameTextRecords } = await import("./basename.js");
263
+ const records = [
264
+ { key: "com.kompass.type", value: options.agentType },
265
+ { key: "com.kompass.categories", value: options.categories.join(",") },
266
+ { key: "com.kompass.description", value: options.description },
267
+ { key: "com.kompass.erc8004.id", value: erc8004Result.agentId.toString() },
268
+ ];
269
+ if (options.pricingModel) records.push({ key: "com.kompass.payment.mode", value: options.pricingModel });
270
+ if (options.pricingRate) records.push({ key: "com.kompass.pricing.rate", value: options.pricingRate });
271
+
272
+ const txHashes = await setBasenameTextRecords(wallet, options.name, records);
273
+ ensRecordTxHashes.push(...txHashes);
274
+ console.log(`[onboard] Set ${records.length} ENS text records separately`);
275
+ } catch (err: any) {
276
+ console.warn(`[onboard] Could not set ENS text records separately: ${err.message}`);
277
+ }
278
+ }
279
+
280
+ // Always track what records we set/would set
281
+ ensRecords["com.kompass.type"] = options.agentType;
282
+ ensRecords["com.kompass.categories"] = options.categories.join(",");
283
+ ensRecords["com.kompass.description"] = options.description;
284
+ ensRecords["com.kompass.erc8004.id"] = erc8004Result.agentId.toString();
285
+ if (options.pricingModel) ensRecords["com.kompass.payment.mode"] = options.pricingModel;
286
+ if (options.pricingRate) ensRecords["com.kompass.pricing.rate"] = options.pricingRate;
287
+ if (options.endpoints?.mcp) ensRecords["com.kompass.endpoint.mcp"] = options.endpoints.mcp;
288
+ if (options.endpoints?.x402) ensRecords["com.kompass.endpoint.x402"] = options.endpoints.x402;
289
+ completedSteps++;
290
+
291
+ // ── Step 5: Register on Kompass Registry (optional) ───────
292
+ let kompassRegistryResult: { txHash: Hex; ensNode: Hex } | undefined;
293
+ if (!options.skipKompassRegistry && basenameResult) {
294
+ console.log(`[onboard] Registering on Kompass Registry...`);
295
+ try {
296
+ // Import registry dynamically to avoid circular deps
297
+ const { register } = await import("./registry.js");
298
+ const { createPublicClient, createWalletClient, http } = await import("viem");
299
+ const { baseSepolia } = await import("viem/chains");
300
+
301
+ // Kompass Registry is on Base Sepolia
302
+ const publicClient = createPublicClient({ chain: baseSepolia, transport: http("https://sepolia.base.org") });
303
+ const walletClient = createWalletClient({
304
+ account: wallet.getAccount(),
305
+ chain: baseSepolia,
306
+ transport: http("https://sepolia.base.org"),
307
+ });
308
+
309
+ const config = {
310
+ publicClient: publicClient as any,
311
+ walletClient: walletClient as any,
312
+ escrowAddress: "0xbc003cE4bd4B272c1C25919B6b8ec379bccdE6ac" as Address,
313
+ registryAddress: "0x94D394362a4BA850673bEF368Ed907996287C7d7" as Address,
314
+ reputationRegistryAddress: "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63" as Address,
315
+ indexerUrl: "https://kompasss.xyz/api",
316
+ };
317
+
318
+ const result = await register(config, {
319
+ ensNode: basenameResult.node,
320
+ ensName: basenameResult.fullName,
321
+ agentType: options.agentType,
322
+ categories: options.categories.join(","),
323
+ description: options.description,
324
+ erc8004Id: erc8004Result.agentId.toString(),
325
+ pricingModel: options.pricingModel,
326
+ pricingRate: options.pricingRate,
327
+ endpointMcp: options.endpoints?.mcp,
328
+ endpointA2a: options.endpoints?.a2a,
329
+ endpointX402: options.endpoints?.x402,
330
+ paymentMode: options.pricingModel as any,
331
+ });
332
+
333
+ kompassRegistryResult = { txHash: result.txHash, ensNode: basenameResult.node };
334
+ console.log(`[onboard] Kompass Registry registration complete`);
335
+ } catch (err: any) {
336
+ console.warn(`[onboard] Kompass Registry registration failed (non-critical): ${err.message}`);
337
+ }
338
+ completedSteps++;
339
+ }
340
+
341
+ return {
342
+ wallet: {
343
+ address,
344
+ network,
345
+ keystorePath: options.walletPath ?? `${process.env.HOME ?? "~"}/.kompass/wallet.json`,
346
+ isNew,
347
+ },
348
+ erc8004: erc8004Result,
349
+ basename: basenameResult,
350
+ ensRecords: {
351
+ txHashes: ensRecordTxHashes,
352
+ records: ensRecords,
353
+ },
354
+ kompassRegistry: kompassRegistryResult,
355
+ agentCard,
356
+ totalSteps,
357
+ completedSteps,
358
+ };
359
+ }
360
+
361
+ // ── Convenience: Full onboard in one call ──────────────────
362
+
363
+ /**
364
+ * Full onboarding: create wallet + register everything.
365
+ * Returns immediately with wallet address if funding is needed.
366
+ */
367
+ export async function onboardAgent(options: OnboardOptions): Promise<OnboardResult> {
368
+ return completeOnboarding(options);
369
+ }
370
+
371
+ // ── Utilities ──────────────────────────────────────────────
372
+
373
+ /**
374
+ * Check if a wallet has enough funds for onboarding.
375
+ */
376
+ export async function checkOnboardingFunds(
377
+ walletPath?: string,
378
+ password?: string,
379
+ ): Promise<{
380
+ address: Address;
381
+ eth: string;
382
+ sufficient: boolean;
383
+ network: string;
384
+ }> {
385
+ const wallet = KompassWallet.load(walletPath, password);
386
+ const balance = await wallet.getBalance();
387
+
388
+ return {
389
+ address: wallet.getAddress(),
390
+ eth: balance.eth,
391
+ sufficient: balance.ethRaw >= 500000000000000n, // 0.0005 ETH minimum
392
+ network: balance.network,
393
+ };
394
+ }
395
+
396
+ /**
397
+ * Estimate the total cost of onboarding.
398
+ */
399
+ export async function estimateOnboardingCost(
400
+ name: string,
401
+ skipBasename: boolean = false,
402
+ ): Promise<{
403
+ basename: string;
404
+ gas: string;
405
+ total: string;
406
+ }> {
407
+ const basenameEth = skipBasename
408
+ ? 0
409
+ : name.length >= 10
410
+ ? 0.0001
411
+ : 0.001;
412
+
413
+ const gasEstimate = 0.001; // ~$2.50 at current prices
414
+
415
+ return {
416
+ basename: skipBasename ? "0" : basenameEth.toString(),
417
+ gas: gasEstimate.toString(),
418
+ total: (basenameEth + gasEstimate).toFixed(4),
419
+ };
420
+ }
package/src/router.ts CHANGED
@@ -95,9 +95,9 @@ export class CapabilityRouter {
95
95
  ? `${options.task}\n\nContext from previous step:\n${options.context.slice(0, 2000)}`
96
96
  : options.task;
97
97
 
98
- // 1. Search all sources
98
+ // 1. Search all sources — use high limit to ensure callable agents aren't cut off
99
99
  const searchResult = await this.aggregator.search(task, {
100
- limit: 30,
100
+ limit: 100,
101
101
  timeout: options.timeout ?? 15000,
102
102
  });
103
103
 
@@ -37,16 +37,17 @@ export const acpAdapter: SourceAdapter = {
37
37
  return offerings.length > 0;
38
38
  });
39
39
 
40
- // Client-side text matching if query provided
40
+ // Client-side text matching if query provided (skip for browse-all)
41
41
  let filtered = withOfferings;
42
- if (query) {
42
+ if (query && query !== "*") {
43
43
  const lower = query.toLowerCase();
44
44
  const terms = lower.split(/\s+/).filter(t => t.length > 2);
45
- filtered = withOfferings.filter((a: any) => {
46
- const text = `${a.name} ${a.description ?? ""} ${(a.jobs ?? a.offerings ?? []).map((o: any) => `${o.name} ${o.description ?? ""}`).join(" ")}`.toLowerCase();
47
- return terms.some(t => text.includes(t));
48
- });
49
- // No matches — return empty, don't pollute results with irrelevant agents
45
+ if (terms.length > 0) {
46
+ filtered = withOfferings.filter((a: any) => {
47
+ const text = `${a.name} ${a.description ?? ""} ${(a.jobs ?? a.offerings ?? []).map((o: any) => `${o.name} ${o.description ?? ""}`).join(" ")}`.toLowerCase();
48
+ return terms.some(t => text.includes(t));
49
+ });
50
+ }
50
51
  }
51
52
 
52
53
  return filtered.slice(0, limit).map(mapAcpAgent);
@@ -27,10 +27,12 @@ export const bankrAdapter: SourceAdapter = {
27
27
  async search(query: string, options?: SourceSearchOptions): Promise<UnifiedAgent[]> {
28
28
  if (!process.env.BANKR_API_KEY) return [];
29
29
 
30
- const lower = query.toLowerCase();
31
- const isRelevant = BANKR_CAPABILITIES.some(cap => lower.includes(cap));
32
-
33
- if (!isRelevant && query.length > 0) return [];
30
+ // Return self for browse-all queries or when query matches capabilities
31
+ if (query && query !== "*") {
32
+ const lower = query.toLowerCase();
33
+ const isRelevant = BANKR_CAPABILITIES.some(cap => lower.includes(cap));
34
+ if (!isRelevant) return [];
35
+ }
34
36
 
35
37
  return [createBankrAgent()];
36
38
  },
@@ -20,7 +20,7 @@ export function createErc8004Adapter(network: "base" | "base-sepolia" = "base"):
20
20
  try {
21
21
  // Search across all chains on 8004scan
22
22
  const url = new URL(`${SCAN_API}/agents`);
23
- if (query) url.searchParams.set("search", query);
23
+ if (query && query !== "*") url.searchParams.set("search", query);
24
24
  url.searchParams.set("limit", String(limit));
25
25
 
26
26
  const res = await fetch(url.toString(), {
@@ -36,7 +36,7 @@ export function createErc8004Adapter(network: "base" | "base-sepolia" = "base"):
36
36
 
37
37
  return agents
38
38
  .filter((a: any) => {
39
- if (!query) return true;
39
+ if (!query || query === "*") return true;
40
40
  const text = `${a.name ?? ""} ${a.description ?? ""}`.toLowerCase();
41
41
  return query.toLowerCase().split(/\s+/).some((t) => text.includes(t));
42
42
  })
@@ -89,6 +89,11 @@ export const locusAdapter: SourceAdapter = {
89
89
 
90
90
  const providers = await fetchProviders(timeout);
91
91
 
92
+ // Return all providers for browse-all queries
93
+ if (!query || query === "*" || terms.length === 0) {
94
+ return providers.slice(0, limit).map(mapLocusProvider);
95
+ }
96
+
92
97
  // Score each provider by relevance
93
98
  const scored = providers.map(provider => {
94
99
  let score = 0;
@@ -61,7 +61,13 @@ async function fetchAllServers(timeout: number): Promise<McpServer[]> {
61
61
  * Much better than the registry's keyword search which only matches names.
62
62
  */
63
63
  function searchLocally(servers: McpServer[], query: string, limit: number): McpServer[] {
64
+ // Return all for browse-all queries
65
+ if (!query || query === "*") {
66
+ return servers.slice(0, limit);
67
+ }
68
+
64
69
  const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
70
+ if (queryTerms.length === 0) return servers.slice(0, limit);
65
71
 
66
72
  return servers
67
73
  .map((server) => {