hive-intelligence 1.9.0 → 1.11.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 +42 -10
- package/agent-skills/packs/hive-mcp/SKILL.md +13 -6
- package/agent-skills/packs/hive-mcp/references/client-install-matrix.md +6 -4
- package/agent-skills/packs/hive-mcp/references/clients.md +6 -3
- package/agent-skills/packs/hive-token-diligence/SKILL.md +2 -0
- package/agent-skills/packs/hive-wallet-investigation/SKILL.md +3 -1
- package/build/{chunk-L4KVJDA7.js → chunk-5IQUEFCD.js} +13 -6
- package/build/{chunk-5QAXJON7.js → chunk-LBJL66AC.js} +5 -2
- package/build/{chunk-WQE3B64R.js → chunk-PUECNLZ2.js} +1 -1
- package/build/{chunk-MVO6LL3H.js → chunk-X4U2IALG.js} +1914 -463
- package/build/cli.js +26 -18
- package/build/{completion-ROZAH667.js → completion-LFOLLOPU.js} +1 -1
- package/build/config-audit-7GC42QX7.js +235 -0
- package/build/{doctor-O2OQX6EX.js → doctor-AFLDGC4O.js} +61 -4
- package/build/{init-all-JXLG7LOX.js → init-all-SPEWION5.js} +9 -9
- package/build/{mcpServer-FXQPSDDC.js → mcpServer-6BQOD6YZ.js} +1 -1
- package/build/monitor-worker.js +4 -3
- package/build/{namespace-XMFFW47T.js → namespace-EU47MWAP.js} +1 -1
- package/build/release.json +4 -4
- package/build/serve-TD63POF2.js +126 -0
- package/build/server.js +2 -2
- package/build/stdio.js +1 -1
- package/build/{tools-NJQPIJ53.js → tools-CCMIU3SO.js} +8 -4
- package/build/{upgrade-3NR3LAGU.js → upgrade-BJADUTO6.js} +1 -1
- package/package.json +12 -12
- package/build/serve-BCKB6DMI.js +0 -49
|
@@ -4457,6 +4457,52 @@ var handler42 = async (client2, args) => {
|
|
|
4457
4457
|
};
|
|
4458
4458
|
var get_timeframe_pools_networks_onchain_ohlcv_default = { metadata: metadata42, tool: tool42, handler: handler42 };
|
|
4459
4459
|
|
|
4460
|
+
// src/tools/onchain/tradeSummary.ts
|
|
4461
|
+
var UPSTREAM_TRADE_CAP = 300;
|
|
4462
|
+
function usd(value) {
|
|
4463
|
+
const n = typeof value === "string" ? Number(value) : value;
|
|
4464
|
+
return typeof n === "number" && Number.isFinite(n) ? n : 0;
|
|
4465
|
+
}
|
|
4466
|
+
function withTradeSummary(payload, minTradeUsd) {
|
|
4467
|
+
const rows = payload?.data;
|
|
4468
|
+
if (!Array.isArray(rows)) return payload;
|
|
4469
|
+
let buys = 0;
|
|
4470
|
+
let sells = 0;
|
|
4471
|
+
let buyVolume = 0;
|
|
4472
|
+
let sellVolume = 0;
|
|
4473
|
+
const timestamps = [];
|
|
4474
|
+
for (const row of rows) {
|
|
4475
|
+
const attrs = row?.attributes ?? {};
|
|
4476
|
+
if (attrs.kind === "buy") {
|
|
4477
|
+
buys += 1;
|
|
4478
|
+
buyVolume += usd(attrs.volume_in_usd);
|
|
4479
|
+
} else if (attrs.kind === "sell") {
|
|
4480
|
+
sells += 1;
|
|
4481
|
+
sellVolume += usd(attrs.volume_in_usd);
|
|
4482
|
+
}
|
|
4483
|
+
if (typeof attrs.block_timestamp === "string") {
|
|
4484
|
+
timestamps.push(attrs.block_timestamp);
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4487
|
+
timestamps.sort();
|
|
4488
|
+
const filtered = usd(minTradeUsd) > 0;
|
|
4489
|
+
return {
|
|
4490
|
+
summary: {
|
|
4491
|
+
trade_count: rows.length,
|
|
4492
|
+
buys,
|
|
4493
|
+
sells,
|
|
4494
|
+
buy_volume_usd: buyVolume,
|
|
4495
|
+
sell_volume_usd: sellVolume,
|
|
4496
|
+
oldest_trade_at: timestamps[0] ?? null,
|
|
4497
|
+
newest_trade_at: timestamps.at(-1) ?? null,
|
|
4498
|
+
min_trade_usd: filtered ? usd(minTradeUsd) : 0,
|
|
4499
|
+
upstream_capped: rows.length >= UPSTREAM_TRADE_CAP,
|
|
4500
|
+
note: `Counts cover every trade GeckoTerminal returned: at most ${UPSTREAM_TRADE_CAP}, newest first, within the last 24h${filtered ? `, trades of at least $${usd(minTradeUsd)} only` : ""}. The data array may be cut to the newest rows by the response budget, so count buys and sells from this summary, not from data. Pool transactions.h24 (get_token_pools) counts every swap and can exceed trade_count when the cap or the minimum-size filter applies.`
|
|
4501
|
+
},
|
|
4502
|
+
...payload
|
|
4503
|
+
};
|
|
4504
|
+
}
|
|
4505
|
+
|
|
4460
4506
|
// src/tools/onchain/networks/pools/trades/get-pools-networks-onchain-trades.ts
|
|
4461
4507
|
var metadata43 = {
|
|
4462
4508
|
resource: "onchain.networks.pools.trades",
|
|
@@ -4555,7 +4601,12 @@ Error Handling:
|
|
|
4555
4601
|
};
|
|
4556
4602
|
var handler43 = async (client2, args) => {
|
|
4557
4603
|
const { pool_address, ...body } = args;
|
|
4558
|
-
return asTextContentResult(
|
|
4604
|
+
return asTextContentResult(
|
|
4605
|
+
withTradeSummary(
|
|
4606
|
+
await client2.onchain.networks.pools.trades.get(pool_address, body),
|
|
4607
|
+
body.trade_volume_in_usd_greater_than
|
|
4608
|
+
)
|
|
4609
|
+
);
|
|
4559
4610
|
};
|
|
4560
4611
|
var get_pools_networks_onchain_trades_default = { metadata: metadata43, tool: tool43, handler: handler43 };
|
|
4561
4612
|
|
|
@@ -4745,6 +4796,8 @@ var handler45 = async (client2, args) => {
|
|
|
4745
4796
|
var get_tokens_networks_onchain_info_default = { metadata: metadata45, tool: tool45, handler: handler45 };
|
|
4746
4797
|
|
|
4747
4798
|
// src/tools/onchain/networks/tokens/top-holders/get-tokens-networks-onchain-top-holders.ts
|
|
4799
|
+
var TOP_HOLDERS_MAX = 50;
|
|
4800
|
+
var SOLANA_TOP_HOLDERS_MAX = 40;
|
|
4748
4801
|
var metadata46 = {
|
|
4749
4802
|
resource: "onchain.networks.tokens.top_holders",
|
|
4750
4803
|
operation: "read",
|
|
@@ -4769,7 +4822,8 @@ Returns a ranked list of addresses holding the largest amounts of a token. Essen
|
|
|
4769
4822
|
Args:
|
|
4770
4823
|
- network (string, required): Network ID (e.g., "eth", "bsc", "solana"). Use coingecko_get_onchain_networks to find valid IDs.
|
|
4771
4824
|
- address (string, required): Token contract address.
|
|
4772
|
-
-
|
|
4825
|
+
- limit (integer, optional): Number of top holders to return, 1-50 (Solana: 1-40, the most Hive's CoinGecko plan serves). Default: the plan maximum.
|
|
4826
|
+
- holders (string, optional): Legacy alias for limit; an integer or "max". Values above the plan maximum, and "max", are capped to it.
|
|
4773
4827
|
- include_pnl_details (boolean, optional): Include realized/unrealized P&L per holder (pnl_usd, cost_basis_usd, avg_buy_price_usd). Default: false.
|
|
4774
4828
|
|
|
4775
4829
|
Returns:
|
|
@@ -4793,9 +4847,8 @@ Returns:
|
|
|
4793
4847
|
}
|
|
4794
4848
|
|
|
4795
4849
|
Examples:
|
|
4796
|
-
- Top
|
|
4797
|
-
- Top
|
|
4798
|
-
- All holders: {"network": "bsc", "address": "0x...", "holders": "max"}
|
|
4850
|
+
- Top 50 USDC holders: {"network": "eth", "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"}
|
|
4851
|
+
- Top 10 holders: {"network": "eth", "address": "0xa0b86991...", "limit": 10}
|
|
4799
4852
|
|
|
4800
4853
|
Use when:
|
|
4801
4854
|
- You need to identify major token holders (whales)
|
|
@@ -4804,6 +4857,7 @@ Use when:
|
|
|
4804
4857
|
- You're tracking institutional or smart money positions
|
|
4805
4858
|
|
|
4806
4859
|
Don't use when:
|
|
4860
|
+
- You need the total holder count or how many holders are above a USD size (use get_token_holder_distribution)
|
|
4807
4861
|
- You need historical holder count (use coingecko_get_onchain_token_holders_chart)
|
|
4808
4862
|
- You need token metadata (use coingecko_get_onchain_token_info)
|
|
4809
4863
|
- You need trading data (use coingecko_get_onchain_token_trades)
|
|
@@ -4825,9 +4879,15 @@ Error Handling:
|
|
|
4825
4879
|
type: "string",
|
|
4826
4880
|
description: "Token contract address. Required."
|
|
4827
4881
|
},
|
|
4882
|
+
limit: {
|
|
4883
|
+
type: "integer",
|
|
4884
|
+
minimum: 1,
|
|
4885
|
+
maximum: TOP_HOLDERS_MAX,
|
|
4886
|
+
description: `Number of top holders to return, 1-${TOP_HOLDERS_MAX} (Solana: 1-${SOLANA_TOP_HOLDERS_MAX}). Default: the plan maximum.`
|
|
4887
|
+
},
|
|
4828
4888
|
holders: {
|
|
4829
4889
|
type: "string",
|
|
4830
|
-
description: '
|
|
4890
|
+
description: 'Legacy alias for limit: an integer or "max". Capped to the plan maximum.'
|
|
4831
4891
|
},
|
|
4832
4892
|
include_pnl_details: {
|
|
4833
4893
|
type: "boolean",
|
|
@@ -4838,10 +4898,19 @@ Error Handling:
|
|
|
4838
4898
|
},
|
|
4839
4899
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
4840
4900
|
};
|
|
4901
|
+
function resolveTopHoldersCount(network, limit, holders) {
|
|
4902
|
+
const cap = String(network ?? "").toLowerCase() === "solana" ? SOLANA_TOP_HOLDERS_MAX : TOP_HOLDERS_MAX;
|
|
4903
|
+
const requested = Number(limit ?? (holders === "max" ? cap : holders ?? cap));
|
|
4904
|
+
if (!Number.isFinite(requested) || requested < 1) return String(cap);
|
|
4905
|
+
return String(Math.min(Math.floor(requested), cap));
|
|
4906
|
+
}
|
|
4841
4907
|
var handler46 = async (client2, args) => {
|
|
4842
|
-
const { address, ...body } = args;
|
|
4908
|
+
const { address, limit, holders, ...body } = args ?? {};
|
|
4843
4909
|
return asTextContentResult(
|
|
4844
|
-
await client2.onchain.networks.tokens.topHolders.get(address,
|
|
4910
|
+
await client2.onchain.networks.tokens.topHolders.get(address, {
|
|
4911
|
+
...body,
|
|
4912
|
+
holders: resolveTopHoldersCount(body.network, limit, holders)
|
|
4913
|
+
})
|
|
4845
4914
|
);
|
|
4846
4915
|
};
|
|
4847
4916
|
var get_tokens_networks_onchain_top_holders_default = { metadata: metadata46, tool: tool46, handler: handler46 };
|
|
@@ -5255,7 +5324,12 @@ Error Handling:
|
|
|
5255
5324
|
};
|
|
5256
5325
|
var handler50 = async (client2, args) => {
|
|
5257
5326
|
const { token_address, ...body } = args;
|
|
5258
|
-
return asTextContentResult(
|
|
5327
|
+
return asTextContentResult(
|
|
5328
|
+
withTradeSummary(
|
|
5329
|
+
await client2.onchain.networks.tokens.trades.get(token_address, body),
|
|
5330
|
+
body.trade_volume_in_usd_greater_than
|
|
5331
|
+
)
|
|
5332
|
+
);
|
|
5259
5333
|
};
|
|
5260
5334
|
var get_tokens_networks_onchain_trades_default = { metadata: metadata50, tool: tool50, handler: handler50 };
|
|
5261
5335
|
|
|
@@ -7762,9 +7836,8 @@ Don't use when:
|
|
|
7762
7836
|
- You need token metadata (use coingecko_get_onchain_token_info)
|
|
7763
7837
|
|
|
7764
7838
|
Error Handling:
|
|
7765
|
-
- Returns 404
|
|
7839
|
+
- Returns 404 for tokens CoinGecko does not track traders for: wrapped native tokens (WETH, wSOL), stablecoins, and tokens created before 2023-09-01. Works on eth, base, solana and other networks for newer tokens.
|
|
7766
7840
|
- Requires CoinGecko's Analyst plan or higher (a provider tier, not a Hive plan)
|
|
7767
|
-
- Not all networks support trader tracking
|
|
7768
7841
|
- Rate limited based on API plan`,
|
|
7769
7842
|
inputSchema: {
|
|
7770
7843
|
type: "object",
|
|
@@ -7803,7 +7876,7 @@ var handler79 = async (client2, args) => {
|
|
|
7803
7876
|
if (error?.status === 404) {
|
|
7804
7877
|
return asTextContentResult(
|
|
7805
7878
|
{
|
|
7806
|
-
error: `Invalid arguments:
|
|
7879
|
+
error: `Invalid arguments: CoinGecko does not track top traders for ${address} on "${network}" (404). It covers tokens created after 2023-09-01 and excludes wrapped native tokens and stablecoins. Use coingecko_get_onchain_token_top_holders or get_token_top_holders for this token.`,
|
|
7807
7880
|
code: "VALIDATION_ERROR",
|
|
7808
7881
|
suggestion: "Call coingecko_get_onchain_token_top_holders with the same network and address."
|
|
7809
7882
|
},
|
|
@@ -8394,17 +8467,18 @@ var handler87 = async (client2, args) => asTextContentResult(
|
|
|
8394
8467
|
var get_total_supply_chart_range_default = { metadata: metadata87, tool: tool87, handler: handler87 };
|
|
8395
8468
|
|
|
8396
8469
|
// src/tools/insights/get-insights.ts
|
|
8470
|
+
var INSIGHTS_SOURCE_NOTE = "AI-generated summary written by CoinGecko; not verified by Hive.";
|
|
8397
8471
|
var metadata88 = {
|
|
8398
8472
|
resource: "insights",
|
|
8399
8473
|
operation: "read",
|
|
8400
|
-
tags: ["coingecko", "coins", "insights", "research", "
|
|
8474
|
+
tags: ["coingecko", "coins", "insights", "research", "ai-generated"],
|
|
8401
8475
|
httpMethod: "get",
|
|
8402
8476
|
httpPath: "/insights",
|
|
8403
8477
|
operationId: "insights"
|
|
8404
8478
|
};
|
|
8405
8479
|
var tool88 = {
|
|
8406
8480
|
name: "coingecko_get_coin_insights",
|
|
8407
|
-
description: `Get CoinGecko's
|
|
8481
|
+
description: `Get CoinGecko's AI-generated coin insights: short summaries of market catalysts, sentiment, and fundamentals. CoinGecko's AI writes these; Hive does not verify them, so treat them as context and confirm facts with market data. Returns a bounded page of at most 20 items.
|
|
8408
8482
|
|
|
8409
8483
|
Args:
|
|
8410
8484
|
- page (integer, optional): Page number from 1 to 20. Default: 1.
|
|
@@ -8414,7 +8488,7 @@ Args:
|
|
|
8414
8488
|
- to (string, optional): End date in YYYY-MM-DD format.
|
|
8415
8489
|
|
|
8416
8490
|
Returns:
|
|
8417
|
-
|
|
8491
|
+
{ source_note, insights: [{ title, description, related_coin_ids, posted_at }] }, bounded to the requested page and page size.
|
|
8418
8492
|
|
|
8419
8493
|
Examples:
|
|
8420
8494
|
- Latest insights: {"page":1,"per_page":10}
|
|
@@ -8429,7 +8503,6 @@ Don't use when:
|
|
|
8429
8503
|
- You need live prices or market rankings (use coingecko_get_coins_markets)
|
|
8430
8504
|
|
|
8431
8505
|
Plan and freshness:
|
|
8432
|
-
- CoinGecko marks this endpoint Enterprise-only.
|
|
8433
8506
|
- CoinGecko documents a 60-second cache/update interval.
|
|
8434
8507
|
- Insight publication time is supplied by CoinGecko; inspect each returned record before treating it as current.`,
|
|
8435
8508
|
inputSchema: {
|
|
@@ -8482,10 +8555,398 @@ var handler88 = async (client2, args) => {
|
|
|
8482
8555
|
from: args?.from,
|
|
8483
8556
|
to: args?.to
|
|
8484
8557
|
};
|
|
8485
|
-
|
|
8558
|
+
const insights = await client2.get("/insights", { query });
|
|
8559
|
+
return asTextContentResult({ source_note: INSIGHTS_SOURCE_NOTE, insights });
|
|
8486
8560
|
};
|
|
8487
8561
|
var get_insights_default = { metadata: metadata88, tool: tool88, handler: handler88 };
|
|
8488
8562
|
|
|
8563
|
+
// src/tools/onchain/wallets/get-wallet-balances.ts
|
|
8564
|
+
var WALLET_BALANCE_NETWORKS = [
|
|
8565
|
+
"eth",
|
|
8566
|
+
"base",
|
|
8567
|
+
"bsc",
|
|
8568
|
+
"polygon_pos",
|
|
8569
|
+
"arbitrum",
|
|
8570
|
+
"optimism",
|
|
8571
|
+
"avax",
|
|
8572
|
+
"linea",
|
|
8573
|
+
"stable",
|
|
8574
|
+
"robinhood",
|
|
8575
|
+
"arc"
|
|
8576
|
+
];
|
|
8577
|
+
var metadata89 = {
|
|
8578
|
+
resource: "onchain.wallets.balances",
|
|
8579
|
+
operation: "read",
|
|
8580
|
+
tags: [
|
|
8581
|
+
"coingecko",
|
|
8582
|
+
"onchain",
|
|
8583
|
+
"wallets",
|
|
8584
|
+
"balances",
|
|
8585
|
+
"portfolio",
|
|
8586
|
+
"geckoterminal"
|
|
8587
|
+
],
|
|
8588
|
+
httpMethod: "get",
|
|
8589
|
+
httpPath: "/onchain/wallets/{address}/balances",
|
|
8590
|
+
operationId: "wallet-balances-address"
|
|
8591
|
+
};
|
|
8592
|
+
var tool89 = {
|
|
8593
|
+
name: "coingecko_get_onchain_wallet_balances",
|
|
8594
|
+
description: `Get a wallet's token balances with USD values and 24h price change across several EVM networks in one call.
|
|
8595
|
+
|
|
8596
|
+
Args:
|
|
8597
|
+
- address (string, required): EVM wallet address (0x\u2026).
|
|
8598
|
+
- networks (string, optional): Comma-separated GeckoTerminal network ids. Default: all supported (${WALLET_BALANCE_NETWORKS.join(",")}). EVM only; Solana is rejected.
|
|
8599
|
+
- page (integer, optional): Page number. Default 1.
|
|
8600
|
+
- per_page (integer, optional): Tokens per page, 1-100. Default 100.
|
|
8601
|
+
|
|
8602
|
+
Returns:
|
|
8603
|
+
data.attributes with total_value_usd, total_holdings, a per-network value breakdown, and balances sorted by value: network, address, token_type ("native" rows use 0xeeee\u2026eeee), symbol, decimals, balance (decimal string), price_usd, value_usd, h24_price_change_percentage, total_reserve_in_usd (pool liquidity behind the price). Numbers are strings.
|
|
8604
|
+
|
|
8605
|
+
Examples:
|
|
8606
|
+
- All supported networks: {"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}
|
|
8607
|
+
- Base only: {"address":"0x\u2026","networks":"base"}
|
|
8608
|
+
|
|
8609
|
+
Use when:
|
|
8610
|
+
- You need a priced multi-chain EVM portfolio in one call.
|
|
8611
|
+
|
|
8612
|
+
Don't use when:
|
|
8613
|
+
- The wallet is on Solana (use helius_get_wallet_balances).
|
|
8614
|
+
- You need realized or unrealized PnL (use get_wallet_pnl).
|
|
8615
|
+
|
|
8616
|
+
Notes:
|
|
8617
|
+
- value_usd uses DEX prices; a large value_usd with a small total_reserve_in_usd usually means an illiquid or spam token.`,
|
|
8618
|
+
inputSchema: {
|
|
8619
|
+
type: "object",
|
|
8620
|
+
properties: {
|
|
8621
|
+
address: {
|
|
8622
|
+
type: "string",
|
|
8623
|
+
minLength: 1,
|
|
8624
|
+
maxLength: 100,
|
|
8625
|
+
description: "EVM wallet address (0x\u2026). Required."
|
|
8626
|
+
},
|
|
8627
|
+
networks: {
|
|
8628
|
+
type: "string",
|
|
8629
|
+
maxLength: 200,
|
|
8630
|
+
description: `Comma-separated network ids. Default: ${WALLET_BALANCE_NETWORKS.join(",")}.`
|
|
8631
|
+
},
|
|
8632
|
+
page: {
|
|
8633
|
+
type: "integer",
|
|
8634
|
+
minimum: 1,
|
|
8635
|
+
maximum: 500,
|
|
8636
|
+
description: "Page number, 1-500. Default 1."
|
|
8637
|
+
},
|
|
8638
|
+
per_page: {
|
|
8639
|
+
type: "integer",
|
|
8640
|
+
minimum: 1,
|
|
8641
|
+
maximum: 100,
|
|
8642
|
+
description: "Tokens per page, 1-100. Default 100."
|
|
8643
|
+
}
|
|
8644
|
+
},
|
|
8645
|
+
required: ["address"],
|
|
8646
|
+
additionalProperties: false
|
|
8647
|
+
},
|
|
8648
|
+
annotations: VOLATILE_READ_ANNOTATIONS
|
|
8649
|
+
};
|
|
8650
|
+
var handler89 = async (client2, args) => {
|
|
8651
|
+
const address = String(args?.address ?? "").trim();
|
|
8652
|
+
const query = {
|
|
8653
|
+
networks: args?.networks ?? WALLET_BALANCE_NETWORKS.join(","),
|
|
8654
|
+
page: args?.page,
|
|
8655
|
+
per_page: args?.per_page ?? 100
|
|
8656
|
+
};
|
|
8657
|
+
return asTextContentResult(
|
|
8658
|
+
await client2.get(
|
|
8659
|
+
`/onchain/wallets/${encodeURIComponent(address)}/balances`,
|
|
8660
|
+
{ query }
|
|
8661
|
+
)
|
|
8662
|
+
);
|
|
8663
|
+
};
|
|
8664
|
+
var get_wallet_balances_default = { metadata: metadata89, tool: tool89, handler: handler89 };
|
|
8665
|
+
|
|
8666
|
+
// src/tools/onchain/wallets/get-wallet-pnl.ts
|
|
8667
|
+
var metadata90 = {
|
|
8668
|
+
resource: "onchain.wallets.pnl",
|
|
8669
|
+
operation: "read",
|
|
8670
|
+
tags: [
|
|
8671
|
+
"coingecko",
|
|
8672
|
+
"onchain",
|
|
8673
|
+
"wallets",
|
|
8674
|
+
"pnl",
|
|
8675
|
+
"profitability",
|
|
8676
|
+
"geckoterminal"
|
|
8677
|
+
],
|
|
8678
|
+
httpMethod: "get",
|
|
8679
|
+
httpPath: "/onchain/wallets/{address}/pnl",
|
|
8680
|
+
operationId: "wallet-pnl-address"
|
|
8681
|
+
};
|
|
8682
|
+
var tool90 = {
|
|
8683
|
+
name: "coingecko_get_onchain_wallet_pnl",
|
|
8684
|
+
description: `Get a wallet's realized and unrealized DEX trading PnL, in total, per network, and per token.
|
|
8685
|
+
|
|
8686
|
+
Args:
|
|
8687
|
+
- address (string, required): Wallet address.
|
|
8688
|
+
- networks (string, optional): Comma-separated GeckoTerminal network ids, all from one VM family (all EVM, or "solana"). Default: ${WALLET_BALANCE_NETWORKS.join(",")}. Any GeckoTerminal network is accepted.
|
|
8689
|
+
- page (integer, optional): Page number. Default 1.
|
|
8690
|
+
- per_page (integer, optional): Tokens per page, 1-300. Default 50.
|
|
8691
|
+
|
|
8692
|
+
Returns:
|
|
8693
|
+
data.attributes with total_realized_pnl_usd, total_unrealized_pnl_usd, total_tokens, a per-network breakdown, and token_stats rows: network, address, symbol, realized_pnl_usd, unrealized_pnl_usd, average_buy_price_usd, average_sell_price_usd, total_buy/sell_count, total_buy/sell_usd. Numbers are strings; unrealized_pnl_usd is null when the token is no longer held.
|
|
8694
|
+
|
|
8695
|
+
Examples:
|
|
8696
|
+
- EVM PnL: {"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}
|
|
8697
|
+
- Solana PnL: {"address":"<base58>","networks":"solana"}
|
|
8698
|
+
|
|
8699
|
+
Use when:
|
|
8700
|
+
- You need to know whether a wallet trades profitably, or which tokens made or lost it money.
|
|
8701
|
+
|
|
8702
|
+
Don't use when:
|
|
8703
|
+
- You need current holdings (use get_wallet_balances).
|
|
8704
|
+
- You need the individual trades (use get_wallet_trades).
|
|
8705
|
+
|
|
8706
|
+
Notes:
|
|
8707
|
+
- PnL is computed from DEX trades CoinGecko indexes; CEX activity and OTC transfers are not counted.`,
|
|
8708
|
+
inputSchema: {
|
|
8709
|
+
type: "object",
|
|
8710
|
+
properties: {
|
|
8711
|
+
address: {
|
|
8712
|
+
type: "string",
|
|
8713
|
+
minLength: 1,
|
|
8714
|
+
maxLength: 100,
|
|
8715
|
+
description: "Wallet address. Required."
|
|
8716
|
+
},
|
|
8717
|
+
networks: {
|
|
8718
|
+
type: "string",
|
|
8719
|
+
maxLength: 200,
|
|
8720
|
+
description: `Comma-separated network ids from one VM family. Default: ${WALLET_BALANCE_NETWORKS.join(",")}.`
|
|
8721
|
+
},
|
|
8722
|
+
page: {
|
|
8723
|
+
type: "integer",
|
|
8724
|
+
minimum: 1,
|
|
8725
|
+
maximum: 500,
|
|
8726
|
+
description: "Page number, 1-500. Default 1."
|
|
8727
|
+
},
|
|
8728
|
+
per_page: {
|
|
8729
|
+
type: "integer",
|
|
8730
|
+
minimum: 1,
|
|
8731
|
+
maximum: 300,
|
|
8732
|
+
description: "Tokens per page, 1-300. Default 50."
|
|
8733
|
+
}
|
|
8734
|
+
},
|
|
8735
|
+
required: ["address"],
|
|
8736
|
+
additionalProperties: false
|
|
8737
|
+
},
|
|
8738
|
+
annotations: VOLATILE_READ_ANNOTATIONS
|
|
8739
|
+
};
|
|
8740
|
+
var handler90 = async (client2, args) => {
|
|
8741
|
+
const address = String(args?.address ?? "").trim();
|
|
8742
|
+
const query = {
|
|
8743
|
+
networks: args?.networks ?? WALLET_BALANCE_NETWORKS.join(","),
|
|
8744
|
+
page: args?.page,
|
|
8745
|
+
per_page: args?.per_page ?? 50
|
|
8746
|
+
};
|
|
8747
|
+
return asTextContentResult(
|
|
8748
|
+
await client2.get(`/onchain/wallets/${encodeURIComponent(address)}/pnl`, {
|
|
8749
|
+
query
|
|
8750
|
+
})
|
|
8751
|
+
);
|
|
8752
|
+
};
|
|
8753
|
+
var get_wallet_pnl_default = { metadata: metadata90, tool: tool90, handler: handler90 };
|
|
8754
|
+
|
|
8755
|
+
// src/tools/onchain/networks/wallets/get-wallet-trades.ts
|
|
8756
|
+
function walletWindowError(args) {
|
|
8757
|
+
if (args?.from == null === (args?.to == null)) return null;
|
|
8758
|
+
return asTextContentResult(
|
|
8759
|
+
{
|
|
8760
|
+
error: "Invalid arguments: pass both from and to (YYYY-MM-DD, at most 30 days apart), or neither for the last 7 days.",
|
|
8761
|
+
code: "VALIDATION_ERROR"
|
|
8762
|
+
},
|
|
8763
|
+
true
|
|
8764
|
+
);
|
|
8765
|
+
}
|
|
8766
|
+
var WALLET_WINDOW_PROPERTIES = {
|
|
8767
|
+
from: {
|
|
8768
|
+
type: "string",
|
|
8769
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
|
|
8770
|
+
description: "Window start, YYYY-MM-DD. Send with to; window at most 30 days. Default: last 7 days."
|
|
8771
|
+
},
|
|
8772
|
+
to: {
|
|
8773
|
+
type: "string",
|
|
8774
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
|
|
8775
|
+
description: "Window end, YYYY-MM-DD. Send with from."
|
|
8776
|
+
},
|
|
8777
|
+
cursor: {
|
|
8778
|
+
type: "string",
|
|
8779
|
+
maxLength: 500,
|
|
8780
|
+
description: "meta.next_cursor from the previous page. Omit for the first page."
|
|
8781
|
+
},
|
|
8782
|
+
per_page: {
|
|
8783
|
+
type: "integer",
|
|
8784
|
+
minimum: 1,
|
|
8785
|
+
maximum: 300,
|
|
8786
|
+
description: "Rows per page, 1-300. Default 100."
|
|
8787
|
+
}
|
|
8788
|
+
};
|
|
8789
|
+
var metadata91 = {
|
|
8790
|
+
resource: "onchain.networks.wallets.trades",
|
|
8791
|
+
operation: "read",
|
|
8792
|
+
tags: ["coingecko", "onchain", "wallets", "trades", "swaps", "geckoterminal"],
|
|
8793
|
+
httpMethod: "get",
|
|
8794
|
+
httpPath: "/onchain/networks/{network}/wallets/{address}/trades",
|
|
8795
|
+
operationId: "wallet-trades-address"
|
|
8796
|
+
};
|
|
8797
|
+
var tool91 = {
|
|
8798
|
+
name: "coingecko_get_onchain_wallet_trades",
|
|
8799
|
+
description: `Get a wallet's DEX buys and sells on one network, with USD prices at execution time.
|
|
8800
|
+
|
|
8801
|
+
Args:
|
|
8802
|
+
- network (string, required): GeckoTerminal network id, e.g. "eth", "base", "solana". Any GeckoTerminal network.
|
|
8803
|
+
- address (string, required): Wallet address.
|
|
8804
|
+
- from, to (string, optional): YYYY-MM-DD window, sent together, at most 30 days. Default: last 7 days.
|
|
8805
|
+
- cursor (string, optional): meta.next_cursor from the previous page.
|
|
8806
|
+
- per_page (integer, optional): 1-300. Default 100.
|
|
8807
|
+
|
|
8808
|
+
Returns:
|
|
8809
|
+
data rows with pool_address, pool_dex, kind (buy/sell), from/to_token_address, from/to_token_amount, price_from/to_in_usd, volume_in_usd, tx_hash, block_timestamp; meta.next_cursor (null on the last page). An empty data array means no DEX trades in the window.
|
|
8810
|
+
|
|
8811
|
+
Examples:
|
|
8812
|
+
- Last 7 days on Base: {"network":"base","address":"0x\u2026"}
|
|
8813
|
+
- September on Ethereum: {"network":"eth","address":"0x\u2026","from":"2026-09-01","to":"2026-09-30"}
|
|
8814
|
+
|
|
8815
|
+
Use when:
|
|
8816
|
+
- You need what a wallet bought and sold, and at what price.
|
|
8817
|
+
|
|
8818
|
+
Don't use when:
|
|
8819
|
+
- You need all token movements including plain transfers (use get_wallet_transfers).
|
|
8820
|
+
- You need the PnL summary (use get_wallet_pnl).`,
|
|
8821
|
+
inputSchema: {
|
|
8822
|
+
type: "object",
|
|
8823
|
+
properties: {
|
|
8824
|
+
network: {
|
|
8825
|
+
type: "string",
|
|
8826
|
+
minLength: 1,
|
|
8827
|
+
maxLength: 60,
|
|
8828
|
+
description: 'GeckoTerminal network id, e.g. "eth". Required.'
|
|
8829
|
+
},
|
|
8830
|
+
address: {
|
|
8831
|
+
type: "string",
|
|
8832
|
+
minLength: 1,
|
|
8833
|
+
maxLength: 100,
|
|
8834
|
+
description: "Wallet address. Required."
|
|
8835
|
+
},
|
|
8836
|
+
...WALLET_WINDOW_PROPERTIES
|
|
8837
|
+
},
|
|
8838
|
+
required: ["network", "address"],
|
|
8839
|
+
additionalProperties: false
|
|
8840
|
+
},
|
|
8841
|
+
annotations: VOLATILE_READ_ANNOTATIONS
|
|
8842
|
+
};
|
|
8843
|
+
var handler91 = async (client2, args) => {
|
|
8844
|
+
const invalid2 = walletWindowError(args);
|
|
8845
|
+
if (invalid2) return invalid2;
|
|
8846
|
+
const network = encodeURIComponent(String(args?.network ?? "").trim());
|
|
8847
|
+
const address = encodeURIComponent(String(args?.address ?? "").trim());
|
|
8848
|
+
const query = {
|
|
8849
|
+
from: args?.from,
|
|
8850
|
+
to: args?.to,
|
|
8851
|
+
cursor: args?.cursor,
|
|
8852
|
+
per_page: args?.per_page ?? 100
|
|
8853
|
+
};
|
|
8854
|
+
return asTextContentResult(
|
|
8855
|
+
await client2.get(`/onchain/networks/${network}/wallets/${address}/trades`, {
|
|
8856
|
+
query
|
|
8857
|
+
})
|
|
8858
|
+
);
|
|
8859
|
+
};
|
|
8860
|
+
var get_wallet_trades_default = { metadata: metadata91, tool: tool91, handler: handler91 };
|
|
8861
|
+
|
|
8862
|
+
// src/tools/onchain/networks/wallets/get-wallet-transfers.ts
|
|
8863
|
+
var metadata92 = {
|
|
8864
|
+
resource: "onchain.networks.wallets.transfers",
|
|
8865
|
+
operation: "read",
|
|
8866
|
+
tags: ["coingecko", "onchain", "wallets", "transfers", "geckoterminal"],
|
|
8867
|
+
httpMethod: "get",
|
|
8868
|
+
httpPath: "/onchain/networks/{network}/wallets/{address}/transfers",
|
|
8869
|
+
operationId: "wallet-transfers-address"
|
|
8870
|
+
};
|
|
8871
|
+
var tool92 = {
|
|
8872
|
+
name: "coingecko_get_onchain_wallet_transfers",
|
|
8873
|
+
description: `Get a wallet's inbound and outbound token transfers on one EVM network.
|
|
8874
|
+
|
|
8875
|
+
Args:
|
|
8876
|
+
- network (string, required): One of ${WALLET_BALANCE_NETWORKS.join(", ")}.
|
|
8877
|
+
- address (string, required): EVM wallet address (0x\u2026).
|
|
8878
|
+
- direction (string, optional): "in" or "out". Default both.
|
|
8879
|
+
- token (string, optional): Only transfers of this token contract address.
|
|
8880
|
+
- from, to (string, optional): YYYY-MM-DD window, sent together, at most 30 days. Default: last 7 days.
|
|
8881
|
+
- cursor (string, optional): meta.next_cursor from the previous page.
|
|
8882
|
+
- per_page (integer, optional): 1-300. Default 100.
|
|
8883
|
+
|
|
8884
|
+
Returns:
|
|
8885
|
+
data rows with block_number, tx_hash, direction, token_address, symbol, decimals, amount (decimal string), from_address, to_address, block_timestamp; meta.next_cursor (null on the last page).
|
|
8886
|
+
|
|
8887
|
+
Examples:
|
|
8888
|
+
- Recent inbound on Ethereum: {"network":"eth","address":"0x\u2026","direction":"in"}
|
|
8889
|
+
|
|
8890
|
+
Use when:
|
|
8891
|
+
- You need who sent a wallet what, or where its tokens went.
|
|
8892
|
+
|
|
8893
|
+
Don't use when:
|
|
8894
|
+
- The wallet is on Solana (use helius_get_wallet_transfers).
|
|
8895
|
+
- You only need DEX buys and sells (use get_wallet_trades).`,
|
|
8896
|
+
inputSchema: {
|
|
8897
|
+
type: "object",
|
|
8898
|
+
properties: {
|
|
8899
|
+
network: {
|
|
8900
|
+
type: "string",
|
|
8901
|
+
minLength: 1,
|
|
8902
|
+
maxLength: 60,
|
|
8903
|
+
description: 'Network id, e.g. "eth". Required.'
|
|
8904
|
+
},
|
|
8905
|
+
address: {
|
|
8906
|
+
type: "string",
|
|
8907
|
+
minLength: 1,
|
|
8908
|
+
maxLength: 100,
|
|
8909
|
+
description: "EVM wallet address. Required."
|
|
8910
|
+
},
|
|
8911
|
+
direction: {
|
|
8912
|
+
type: "string",
|
|
8913
|
+
enum: ["in", "out"],
|
|
8914
|
+
description: "Transfer direction. Default both."
|
|
8915
|
+
},
|
|
8916
|
+
token: {
|
|
8917
|
+
type: "string",
|
|
8918
|
+
maxLength: 100,
|
|
8919
|
+
description: "Token contract address filter."
|
|
8920
|
+
},
|
|
8921
|
+
...WALLET_WINDOW_PROPERTIES
|
|
8922
|
+
},
|
|
8923
|
+
required: ["network", "address"],
|
|
8924
|
+
additionalProperties: false
|
|
8925
|
+
},
|
|
8926
|
+
annotations: VOLATILE_READ_ANNOTATIONS
|
|
8927
|
+
};
|
|
8928
|
+
var handler92 = async (client2, args) => {
|
|
8929
|
+
const invalid2 = walletWindowError(args);
|
|
8930
|
+
if (invalid2) return invalid2;
|
|
8931
|
+
const network = encodeURIComponent(String(args?.network ?? "").trim());
|
|
8932
|
+
const address = encodeURIComponent(String(args?.address ?? "").trim());
|
|
8933
|
+
const query = {
|
|
8934
|
+
direction: args?.direction,
|
|
8935
|
+
token: args?.token,
|
|
8936
|
+
from: args?.from,
|
|
8937
|
+
to: args?.to,
|
|
8938
|
+
cursor: args?.cursor,
|
|
8939
|
+
per_page: args?.per_page ?? 100
|
|
8940
|
+
};
|
|
8941
|
+
return asTextContentResult(
|
|
8942
|
+
await client2.get(
|
|
8943
|
+
`/onchain/networks/${network}/wallets/${address}/transfers`,
|
|
8944
|
+
{ query }
|
|
8945
|
+
)
|
|
8946
|
+
);
|
|
8947
|
+
};
|
|
8948
|
+
var get_wallet_transfers_default = { metadata: metadata92, tool: tool92, handler: handler92 };
|
|
8949
|
+
|
|
8489
8950
|
// src/tools/rwas/query.ts
|
|
8490
8951
|
function pickQuery(args, keys) {
|
|
8491
8952
|
const query = {};
|
|
@@ -8522,7 +8983,7 @@ var PRECISION_VALUES = [
|
|
|
8522
8983
|
];
|
|
8523
8984
|
|
|
8524
8985
|
// src/tools/rwas/get-rwas-list.ts
|
|
8525
|
-
var
|
|
8986
|
+
var metadata93 = {
|
|
8526
8987
|
resource: "rwas",
|
|
8527
8988
|
operation: "read",
|
|
8528
8989
|
tags: ["coingecko", "rwa", "tokenized", "stocks", "commodities", "etf", "list"],
|
|
@@ -8530,7 +8991,7 @@ var metadata89 = {
|
|
|
8530
8991
|
httpPath: "/rwas/list",
|
|
8531
8992
|
operationId: "rwas-list"
|
|
8532
8993
|
};
|
|
8533
|
-
var
|
|
8994
|
+
var tool93 = {
|
|
8534
8995
|
name: "coingecko_get_rwa_list",
|
|
8535
8996
|
description: `List every tokenized real-world asset (RWA) that CoinGecko tracks: tokenized stocks, commodities, and ETFs with spot market data. Use it to resolve the RWA asset IDs the other coingecko_get_rwa_* tools take.
|
|
8536
8997
|
|
|
@@ -8577,15 +9038,15 @@ Plan and freshness:
|
|
|
8577
9038
|
},
|
|
8578
9039
|
annotations: READ_ONLY_ANNOTATIONS
|
|
8579
9040
|
};
|
|
8580
|
-
var
|
|
9041
|
+
var handler93 = async (client2, args) => {
|
|
8581
9042
|
return asTextContentResult(
|
|
8582
9043
|
await client2.get("/rwas/list", { query: pickQuery(args, ["asset_type"]) })
|
|
8583
9044
|
);
|
|
8584
9045
|
};
|
|
8585
|
-
var get_rwas_list_default = { metadata:
|
|
9046
|
+
var get_rwas_list_default = { metadata: metadata93, tool: tool93, handler: handler93 };
|
|
8586
9047
|
|
|
8587
9048
|
// src/tools/rwas/get-rwas-markets.ts
|
|
8588
|
-
var
|
|
9049
|
+
var metadata94 = {
|
|
8589
9050
|
resource: "rwas.markets",
|
|
8590
9051
|
operation: "read",
|
|
8591
9052
|
tags: ["coingecko", "rwa", "tokenized", "stocks", "commodities", "etf", "markets", "price"],
|
|
@@ -8606,7 +9067,7 @@ var QUERY_KEYS = [
|
|
|
8606
9067
|
"price_change_percentage",
|
|
8607
9068
|
"sparkline"
|
|
8608
9069
|
];
|
|
8609
|
-
var
|
|
9070
|
+
var tool94 = {
|
|
8610
9071
|
name: "coingecko_get_rwa_markets",
|
|
8611
9072
|
description: `Get CoinGecko spot market data for tokenized real-world assets (RWAs): tokenized stocks, commodities, and ETFs, with USD price, market cap, and 24h volume aggregated across every token that wraps each asset. This is spot data for the tokenized versions, not the traditional exchange quote and not perpetual futures.
|
|
8612
9073
|
|
|
@@ -8740,15 +9201,15 @@ Plan and freshness:
|
|
|
8740
9201
|
},
|
|
8741
9202
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
8742
9203
|
};
|
|
8743
|
-
var
|
|
9204
|
+
var handler94 = async (client2, args) => {
|
|
8744
9205
|
return asTextContentResult(
|
|
8745
9206
|
await client2.get("/rwas/markets", { query: pickQuery(args, QUERY_KEYS) })
|
|
8746
9207
|
);
|
|
8747
9208
|
};
|
|
8748
|
-
var get_rwas_markets_default = { metadata:
|
|
9209
|
+
var get_rwas_markets_default = { metadata: metadata94, tool: tool94, handler: handler94 };
|
|
8749
9210
|
|
|
8750
9211
|
// src/tools/rwas/get-rwas-asset.ts
|
|
8751
|
-
var
|
|
9212
|
+
var metadata95 = {
|
|
8752
9213
|
resource: "rwas",
|
|
8753
9214
|
operation: "read",
|
|
8754
9215
|
tags: ["coingecko", "rwa", "tokenized", "stocks", "commodities", "etf", "details", "tokens"],
|
|
@@ -8757,7 +9218,7 @@ var metadata91 = {
|
|
|
8757
9218
|
operationId: "rwas-id"
|
|
8758
9219
|
};
|
|
8759
9220
|
var QUERY_KEYS2 = ["sparkline", "tokenized_market_data", "tokens"];
|
|
8760
|
-
var
|
|
9221
|
+
var tool95 = {
|
|
8761
9222
|
name: "coingecko_get_rwa_asset",
|
|
8762
9223
|
description: `Get one tokenized real-world asset (RWA) from CoinGecko by ID: a tokenized stock, commodity, or ETF, with its metadata, aggregated spot market data, and the list of tokens that wrap it, each with contract addresses per chain and its issuer.
|
|
8763
9224
|
|
|
@@ -8834,17 +9295,17 @@ Plan and freshness:
|
|
|
8834
9295
|
},
|
|
8835
9296
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
8836
9297
|
};
|
|
8837
|
-
var
|
|
9298
|
+
var handler95 = async (client2, args) => {
|
|
8838
9299
|
return asTextContentResult(
|
|
8839
9300
|
await client2.get(`/rwas/${pathId(args)}`, {
|
|
8840
9301
|
query: pickQuery(args, QUERY_KEYS2)
|
|
8841
9302
|
})
|
|
8842
9303
|
);
|
|
8843
9304
|
};
|
|
8844
|
-
var get_rwas_asset_default = { metadata:
|
|
9305
|
+
var get_rwas_asset_default = { metadata: metadata95, tool: tool95, handler: handler95 };
|
|
8845
9306
|
|
|
8846
9307
|
// src/tools/rwas/get-rwas-tickers.ts
|
|
8847
|
-
var
|
|
9308
|
+
var metadata96 = {
|
|
8848
9309
|
resource: "rwas.tickers",
|
|
8849
9310
|
operation: "read",
|
|
8850
9311
|
tags: ["coingecko", "rwa", "tokenized", "stocks", "commodities", "etf", "tickers", "exchanges", "liquidity"],
|
|
@@ -8860,7 +9321,7 @@ var QUERY_KEYS3 = [
|
|
|
8860
9321
|
"order",
|
|
8861
9322
|
"page"
|
|
8862
9323
|
];
|
|
8863
|
-
var
|
|
9324
|
+
var tool96 = {
|
|
8864
9325
|
name: "coingecko_get_rwa_tickers",
|
|
8865
9326
|
description: `Get exchange tickers for one tokenized real-world asset (RWA) on CoinGecko: every spot pair, across centralized and decentralized exchanges, for the tokens that wrap a given stock, commodity, or ETF. Requires a CoinGecko Basic plan or above.
|
|
8866
9327
|
|
|
@@ -8950,17 +9411,17 @@ Plan and freshness:
|
|
|
8950
9411
|
},
|
|
8951
9412
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
8952
9413
|
};
|
|
8953
|
-
var
|
|
9414
|
+
var handler96 = async (client2, args) => {
|
|
8954
9415
|
return asTextContentResult(
|
|
8955
9416
|
await client2.get(`/rwas/${pathId(args)}/tickers`, {
|
|
8956
9417
|
query: pickQuery(args, QUERY_KEYS3)
|
|
8957
9418
|
})
|
|
8958
9419
|
);
|
|
8959
9420
|
};
|
|
8960
|
-
var get_rwas_tickers_default = { metadata:
|
|
9421
|
+
var get_rwas_tickers_default = { metadata: metadata96, tool: tool96, handler: handler96 };
|
|
8961
9422
|
|
|
8962
9423
|
// src/tools/rwas/get-rwas-market-chart.ts
|
|
8963
|
-
var
|
|
9424
|
+
var metadata97 = {
|
|
8964
9425
|
resource: "rwas.market_chart",
|
|
8965
9426
|
operation: "read",
|
|
8966
9427
|
tags: ["coingecko", "rwa", "tokenized", "stocks", "commodities", "etf", "chart", "historical"],
|
|
@@ -8969,7 +9430,7 @@ var metadata93 = {
|
|
|
8969
9430
|
operationId: "rwas-id-market-chart"
|
|
8970
9431
|
};
|
|
8971
9432
|
var QUERY_KEYS4 = ["days", "interval", "precision"];
|
|
8972
|
-
var
|
|
9433
|
+
var tool97 = {
|
|
8973
9434
|
name: "coingecko_get_rwa_market_chart",
|
|
8974
9435
|
description: `Get historical spot price, market cap, and volume series for one tokenized real-world asset (RWA) on CoinGecko: a tokenized stock, commodity, or ETF, aggregated across the tokens that wrap it. Requires a CoinGecko Basic plan or above.
|
|
8975
9436
|
|
|
@@ -9037,17 +9498,17 @@ Plan and freshness:
|
|
|
9037
9498
|
},
|
|
9038
9499
|
annotations: HISTORICAL_ANNOTATIONS
|
|
9039
9500
|
};
|
|
9040
|
-
var
|
|
9501
|
+
var handler97 = async (client2, args) => {
|
|
9041
9502
|
const query = pickQuery(args, QUERY_KEYS4);
|
|
9042
9503
|
if (query.days === void 0) query.days = "1";
|
|
9043
9504
|
return asTextContentResult(
|
|
9044
9505
|
await client2.get(`/rwas/${pathId(args)}/market_chart`, { query })
|
|
9045
9506
|
);
|
|
9046
9507
|
};
|
|
9047
|
-
var get_rwas_market_chart_default = { metadata:
|
|
9508
|
+
var get_rwas_market_chart_default = { metadata: metadata97, tool: tool97, handler: handler97 };
|
|
9048
9509
|
|
|
9049
9510
|
// src/tools/rwas/get-rwas-issuers.ts
|
|
9050
|
-
var
|
|
9511
|
+
var metadata98 = {
|
|
9051
9512
|
resource: "rwas.issuers",
|
|
9052
9513
|
operation: "read",
|
|
9053
9514
|
tags: ["coingecko", "rwa", "tokenized", "issuers", "list"],
|
|
@@ -9055,7 +9516,7 @@ var metadata94 = {
|
|
|
9055
9516
|
httpPath: "/rwas/issuers/list",
|
|
9056
9517
|
operationId: "rwas-issuers-list"
|
|
9057
9518
|
};
|
|
9058
|
-
var
|
|
9519
|
+
var tool98 = {
|
|
9059
9520
|
name: "coingecko_get_rwa_issuers",
|
|
9060
9521
|
description: `List every issuer of tokenized real-world assets (RWAs) that CoinGecko tracks with spot market data: the companies and protocols that mint tokenized stocks, commodities, and ETFs, such as Ondo, Backed, Paxos, Dinari, and Robinhood. Use it to resolve issuer IDs for coingecko_get_rwa_issuer and for the issuer filters on coingecko_get_rwa_markets and coingecko_get_rwa_tickers.
|
|
9061
9522
|
|
|
@@ -9093,13 +9554,13 @@ Plan and freshness:
|
|
|
9093
9554
|
},
|
|
9094
9555
|
annotations: READ_ONLY_ANNOTATIONS
|
|
9095
9556
|
};
|
|
9096
|
-
var
|
|
9557
|
+
var handler98 = async (client2, _args) => {
|
|
9097
9558
|
return asTextContentResult(await client2.get("/rwas/issuers/list"));
|
|
9098
9559
|
};
|
|
9099
|
-
var get_rwas_issuers_default = { metadata:
|
|
9560
|
+
var get_rwas_issuers_default = { metadata: metadata98, tool: tool98, handler: handler98 };
|
|
9100
9561
|
|
|
9101
9562
|
// src/tools/rwas/get-rwas-issuer.ts
|
|
9102
|
-
var
|
|
9563
|
+
var metadata99 = {
|
|
9103
9564
|
resource: "rwas.issuers",
|
|
9104
9565
|
operation: "read",
|
|
9105
9566
|
tags: ["coingecko", "rwa", "tokenized", "issuers", "details", "tokens"],
|
|
@@ -9107,7 +9568,7 @@ var metadata95 = {
|
|
|
9107
9568
|
httpPath: "/rwas/issuers/{id}",
|
|
9108
9569
|
operationId: "rwas-issuers-id"
|
|
9109
9570
|
};
|
|
9110
|
-
var
|
|
9571
|
+
var tool99 = {
|
|
9111
9572
|
name: "coingecko_get_rwa_issuer",
|
|
9112
9573
|
description: `Get one issuer of tokenized real-world assets (RWAs) from CoinGecko by ID, with its aggregated spot market cap and 24h volume across every tokenized stock, commodity, and ETF it has minted, plus the full token catalog with per-chain contract addresses.
|
|
9113
9574
|
|
|
@@ -9167,12 +9628,12 @@ Plan and freshness:
|
|
|
9167
9628
|
},
|
|
9168
9629
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
9169
9630
|
};
|
|
9170
|
-
var
|
|
9631
|
+
var handler99 = async (client2, args) => {
|
|
9171
9632
|
return asTextContentResult(
|
|
9172
9633
|
await client2.get(`/rwas/issuers/${pathId(args)}`)
|
|
9173
9634
|
);
|
|
9174
9635
|
};
|
|
9175
|
-
var get_rwas_issuer_default = { metadata:
|
|
9636
|
+
var get_rwas_issuer_default = { metadata: metadata99, tool: tool99, handler: handler99 };
|
|
9176
9637
|
|
|
9177
9638
|
// src/db/supabase.ts
|
|
9178
9639
|
import { createClient } from "@supabase/supabase-js";
|
|
@@ -9341,24 +9802,24 @@ var secureLogger = {
|
|
|
9341
9802
|
/**
|
|
9342
9803
|
* Log authentication events without exposing tokens
|
|
9343
9804
|
*/
|
|
9344
|
-
authEvent: (event, success,
|
|
9805
|
+
authEvent: (event, success, metadata104) => {
|
|
9345
9806
|
const logData = {
|
|
9346
9807
|
event,
|
|
9347
9808
|
success,
|
|
9348
9809
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9349
|
-
...sanitizeObject(
|
|
9810
|
+
...sanitizeObject(metadata104 || {})
|
|
9350
9811
|
};
|
|
9351
9812
|
console.error(formatLogMessage("AUTH", event, logData));
|
|
9352
9813
|
},
|
|
9353
9814
|
/**
|
|
9354
9815
|
* Log MCP tool calls for analytics
|
|
9355
9816
|
*/
|
|
9356
|
-
toolCall: (toolName, success, durationMs,
|
|
9817
|
+
toolCall: (toolName, success, durationMs, metadata104) => {
|
|
9357
9818
|
const logData = {
|
|
9358
9819
|
tool: toolName,
|
|
9359
9820
|
success,
|
|
9360
9821
|
durationMs,
|
|
9361
|
-
...sanitizeObject(
|
|
9822
|
+
...sanitizeObject(metadata104 || {})
|
|
9362
9823
|
};
|
|
9363
9824
|
console.error(
|
|
9364
9825
|
formatLogMessage(
|
|
@@ -9371,12 +9832,12 @@ var secureLogger = {
|
|
|
9371
9832
|
/**
|
|
9372
9833
|
* Log request/response for debugging (sanitized)
|
|
9373
9834
|
*/
|
|
9374
|
-
request: (method, path, statusCode,
|
|
9835
|
+
request: (method, path, statusCode, metadata104) => {
|
|
9375
9836
|
const logData = {
|
|
9376
9837
|
method,
|
|
9377
9838
|
path,
|
|
9378
9839
|
statusCode,
|
|
9379
|
-
...sanitizeObject(
|
|
9840
|
+
...sanitizeObject(metadata104 || {})
|
|
9380
9841
|
};
|
|
9381
9842
|
console.error(formatLogMessage("REQUEST", `${method} ${path}`, logData));
|
|
9382
9843
|
}
|
|
@@ -10018,8 +10479,8 @@ function canonicalJson2(value) {
|
|
|
10018
10479
|
}
|
|
10019
10480
|
return JSON.stringify(value) ?? "null";
|
|
10020
10481
|
}
|
|
10021
|
-
function cleanMonitorMetadata(
|
|
10022
|
-
const cleaned = { ...
|
|
10482
|
+
function cleanMonitorMetadata(metadata104) {
|
|
10483
|
+
const cleaned = { ...metadata104 ?? {} };
|
|
10023
10484
|
delete cleaned.idempotency_key;
|
|
10024
10485
|
delete cleaned.idempotency_fingerprint;
|
|
10025
10486
|
delete cleaned.legacy_duplicate_idempotency_key;
|
|
@@ -10055,15 +10516,15 @@ function monitorCreateFingerprint(input) {
|
|
|
10055
10516
|
).digest("hex");
|
|
10056
10517
|
}
|
|
10057
10518
|
function existingMonitorFingerprint(existing) {
|
|
10058
|
-
const
|
|
10059
|
-
const stored =
|
|
10519
|
+
const metadata104 = existing.metadata ?? {};
|
|
10520
|
+
const stored = metadata104.idempotency_fingerprint;
|
|
10060
10521
|
if (typeof stored === "string" && /^[a-f0-9]{64}$/.test(stored)) {
|
|
10061
10522
|
return stored;
|
|
10062
10523
|
}
|
|
10063
10524
|
return monitorCreateFingerprint({
|
|
10064
10525
|
cadence: existing.cadence,
|
|
10065
10526
|
kind: existing.kind,
|
|
10066
|
-
metadata:
|
|
10527
|
+
metadata: metadata104,
|
|
10067
10528
|
name: existing.name,
|
|
10068
10529
|
nextRunAt: existing.next_run_at,
|
|
10069
10530
|
rules: existing.rules,
|
|
@@ -10833,7 +11294,7 @@ var hiveCreateMonitor = {
|
|
|
10833
11294
|
const target = parseMonitorTarget(args?.target);
|
|
10834
11295
|
const rules = parseMonitorRules(args?.rules);
|
|
10835
11296
|
const status = parseMonitorStatus(args?.status);
|
|
10836
|
-
const
|
|
11297
|
+
const metadata104 = parseMonitorMetadata(args?.metadata);
|
|
10837
11298
|
const name = stringArg(args, "name");
|
|
10838
11299
|
if (!name) {
|
|
10839
11300
|
return result(
|
|
@@ -10881,7 +11342,7 @@ var hiveCreateMonitor = {
|
|
|
10881
11342
|
"monitor"
|
|
10882
11343
|
);
|
|
10883
11344
|
}
|
|
10884
|
-
if (args?.metadata !== void 0 && !
|
|
11345
|
+
if (args?.metadata !== void 0 && !metadata104) {
|
|
10885
11346
|
return result(
|
|
10886
11347
|
{
|
|
10887
11348
|
ok: false,
|
|
@@ -10913,7 +11374,7 @@ var hiveCreateMonitor = {
|
|
|
10913
11374
|
cadence: stringArg(args, "cadence"),
|
|
10914
11375
|
status,
|
|
10915
11376
|
nextRunAt: stringArg(args, "next_run_at") ?? null,
|
|
10916
|
-
metadata:
|
|
11377
|
+
metadata: metadata104,
|
|
10917
11378
|
idempotencyKey
|
|
10918
11379
|
}),
|
|
10919
11380
|
"monitor"
|
|
@@ -11100,7 +11561,7 @@ var hiveUpdateMonitor = {
|
|
|
11100
11561
|
const status = parseMonitorStatus(args?.status);
|
|
11101
11562
|
const target = args?.target === void 0 ? void 0 : parseMonitorTarget(args?.target);
|
|
11102
11563
|
const rules = args?.rules === void 0 ? void 0 : parseMonitorRules(args?.rules);
|
|
11103
|
-
const
|
|
11564
|
+
const metadata104 = args?.metadata === void 0 ? void 0 : parseMonitorMetadata(args?.metadata);
|
|
11104
11565
|
if (args?.status !== void 0 && !status) {
|
|
11105
11566
|
return result(
|
|
11106
11567
|
{
|
|
@@ -11131,7 +11592,7 @@ var hiveUpdateMonitor = {
|
|
|
11131
11592
|
"monitor"
|
|
11132
11593
|
);
|
|
11133
11594
|
}
|
|
11134
|
-
if (args?.metadata !== void 0 && !
|
|
11595
|
+
if (args?.metadata !== void 0 && !metadata104) {
|
|
11135
11596
|
return result(
|
|
11136
11597
|
{
|
|
11137
11598
|
ok: false,
|
|
@@ -11152,7 +11613,7 @@ var hiveUpdateMonitor = {
|
|
|
11152
11613
|
rules,
|
|
11153
11614
|
cadence: stringArg(args, "cadence"),
|
|
11154
11615
|
nextRunAt: args?.next_run_at === void 0 ? void 0 : stringArg(args, "next_run_at") ?? null,
|
|
11155
|
-
metadata:
|
|
11616
|
+
metadata: metadata104
|
|
11156
11617
|
}),
|
|
11157
11618
|
"monitor"
|
|
11158
11619
|
);
|
|
@@ -12044,14 +12505,14 @@ function validateFeedbackInput(input) {
|
|
|
12044
12505
|
`Invalid arguments: message is ${message.length} characters; the limit is ${FEEDBACK_MAX_MESSAGE_CHARS}.`
|
|
12045
12506
|
);
|
|
12046
12507
|
}
|
|
12047
|
-
let
|
|
12508
|
+
let tool100 = null;
|
|
12048
12509
|
if (input.tool !== void 0 && input.tool !== null) {
|
|
12049
12510
|
if (typeof input.tool !== "string" || !isToolNameShaped(input.tool)) {
|
|
12050
12511
|
throw new FeedbackValidationError(
|
|
12051
12512
|
"Invalid arguments: tool must be a Hive tool name (lowercase letters, digits and underscores, 3 to 128 characters) or omitted."
|
|
12052
12513
|
);
|
|
12053
12514
|
}
|
|
12054
|
-
|
|
12515
|
+
tool100 = input.tool;
|
|
12055
12516
|
}
|
|
12056
12517
|
let receiptId = null;
|
|
12057
12518
|
if (input.receiptId !== void 0 && input.receiptId !== null) {
|
|
@@ -12062,7 +12523,7 @@ function validateFeedbackInput(input) {
|
|
|
12062
12523
|
}
|
|
12063
12524
|
receiptId = input.receiptId.toLowerCase();
|
|
12064
12525
|
}
|
|
12065
|
-
return { message: scrubFeedbackText(message), tool:
|
|
12526
|
+
return { message: scrubFeedbackText(message), tool: tool100, receiptId };
|
|
12066
12527
|
}
|
|
12067
12528
|
function withTimeout(promise, ms) {
|
|
12068
12529
|
return new Promise((resolve, reject) => {
|
|
@@ -12367,6 +12828,10 @@ function getAllEndpoints() {
|
|
|
12367
12828
|
list.push(get_total_supply_chart_default);
|
|
12368
12829
|
list.push(get_total_supply_chart_range_default);
|
|
12369
12830
|
list.push(get_insights_default);
|
|
12831
|
+
list.push(get_wallet_balances_default);
|
|
12832
|
+
list.push(get_wallet_pnl_default);
|
|
12833
|
+
list.push(get_wallet_trades_default);
|
|
12834
|
+
list.push(get_wallet_transfers_default);
|
|
12370
12835
|
list.push(get_rwas_list_default);
|
|
12371
12836
|
list.push(get_rwas_markets_default);
|
|
12372
12837
|
list.push(get_rwas_asset_default);
|
|
@@ -16573,6 +17038,30 @@ var wrapper = {
|
|
|
16573
17038
|
oldToolName: "coingecko_get_coin_insights"
|
|
16574
17039
|
}
|
|
16575
17040
|
},
|
|
17041
|
+
{
|
|
17042
|
+
tool: {
|
|
17043
|
+
name: "get_wallet_balances",
|
|
17044
|
+
oldToolName: "coingecko_get_onchain_wallet_balances"
|
|
17045
|
+
}
|
|
17046
|
+
},
|
|
17047
|
+
{
|
|
17048
|
+
tool: {
|
|
17049
|
+
name: "get_wallet_pnl",
|
|
17050
|
+
oldToolName: "coingecko_get_onchain_wallet_pnl"
|
|
17051
|
+
}
|
|
17052
|
+
},
|
|
17053
|
+
{
|
|
17054
|
+
tool: {
|
|
17055
|
+
name: "get_wallet_trades",
|
|
17056
|
+
oldToolName: "coingecko_get_onchain_wallet_trades"
|
|
17057
|
+
}
|
|
17058
|
+
},
|
|
17059
|
+
{
|
|
17060
|
+
tool: {
|
|
17061
|
+
name: "get_wallet_transfers",
|
|
17062
|
+
oldToolName: "coingecko_get_onchain_wallet_transfers"
|
|
17063
|
+
}
|
|
17064
|
+
},
|
|
16576
17065
|
{
|
|
16577
17066
|
tool: {
|
|
16578
17067
|
name: "get_token_lists",
|
|
@@ -18117,6 +18606,16 @@ var wrapper = {
|
|
|
18117
18606
|
oldToolName: "ccxt_fetch_convert_quote"
|
|
18118
18607
|
}
|
|
18119
18608
|
}
|
|
18609
|
+
],
|
|
18610
|
+
// Blockscout tools describe themselves; these entries only admit the
|
|
18611
|
+
// unprefixed name (src/tools/blockscout/tools.ts).
|
|
18612
|
+
getAllBlockscoutEndpoints: [
|
|
18613
|
+
{
|
|
18614
|
+
tool: {
|
|
18615
|
+
name: "get_token_holder_distribution",
|
|
18616
|
+
oldToolName: "get_token_holder_distribution"
|
|
18617
|
+
}
|
|
18618
|
+
}
|
|
18120
18619
|
]
|
|
18121
18620
|
};
|
|
18122
18621
|
|
|
@@ -18139,10 +18638,10 @@ function setupLogger() {
|
|
|
18139
18638
|
const consoleFormat = winston.format.combine(
|
|
18140
18639
|
winston.format.colorize(),
|
|
18141
18640
|
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
|
18142
|
-
winston.format.printf(({ timestamp, level, message, ...
|
|
18641
|
+
winston.format.printf(({ timestamp, level, message, ...metadata104 }) => {
|
|
18143
18642
|
let msg = `${timestamp} [${level}]: ${message}`;
|
|
18144
|
-
if (Object.keys(
|
|
18145
|
-
msg += ` ${JSON.stringify(
|
|
18643
|
+
if (Object.keys(metadata104).length > 0) {
|
|
18644
|
+
msg += ` ${JSON.stringify(metadata104)}`;
|
|
18146
18645
|
}
|
|
18147
18646
|
return msg;
|
|
18148
18647
|
})
|
|
@@ -26685,10 +27184,10 @@ function setupTools(apiClient) {
|
|
|
26685
27184
|
...derivativesVolumesTools(apiClient),
|
|
26686
27185
|
...metadataTools(apiClient)
|
|
26687
27186
|
];
|
|
26688
|
-
toolCategories.forEach((
|
|
26689
|
-
const execute =
|
|
26690
|
-
tools5[
|
|
26691
|
-
...
|
|
27187
|
+
toolCategories.forEach((tool100) => {
|
|
27188
|
+
const execute = tool100.execute.bind(tool100);
|
|
27189
|
+
tools5[tool100.name] = {
|
|
27190
|
+
...tool100,
|
|
26692
27191
|
execute: async (args) => normalizeMcpErrorResult(await execute(args))
|
|
26693
27192
|
};
|
|
26694
27193
|
});
|
|
@@ -30771,7 +31270,7 @@ var handlers2 = {
|
|
|
30771
31270
|
"ccxt_fetch_open_interest_multi"
|
|
30772
31271
|
)
|
|
30773
31272
|
};
|
|
30774
|
-
var
|
|
31273
|
+
var metadata100 = {
|
|
30775
31274
|
fetchMarkets: fetchMarketsMetadata,
|
|
30776
31275
|
fetchCurrencies: fetchCurrenciesMetadata,
|
|
30777
31276
|
fetchTicker: fetchTickerMetadata,
|
|
@@ -30887,8 +31386,8 @@ var CCXT_REQUIRED_BATCH_FILTERS = {
|
|
|
30887
31386
|
ccxt_fetch_borrow_rate_histories: "codes",
|
|
30888
31387
|
ccxt_fetch_all_greeks: "symbols"
|
|
30889
31388
|
};
|
|
30890
|
-
function withCcxtBounds(
|
|
30891
|
-
const schema =
|
|
31389
|
+
function withCcxtBounds(tool100) {
|
|
31390
|
+
const schema = tool100.inputSchema;
|
|
30892
31391
|
const properties = Object.fromEntries(
|
|
30893
31392
|
Object.entries(schema.properties || {}).map(
|
|
30894
31393
|
([name, rawProperty]) => {
|
|
@@ -30907,13 +31406,13 @@ function withCcxtBounds(tool96) {
|
|
|
30907
31406
|
)
|
|
30908
31407
|
);
|
|
30909
31408
|
const required = new Set(schema.required || []);
|
|
30910
|
-
const explicitFilter = CCXT_REQUIRED_BATCH_FILTERS[
|
|
31409
|
+
const explicitFilter = CCXT_REQUIRED_BATCH_FILTERS[tool100.name];
|
|
30911
31410
|
if (explicitFilter) required.add(explicitFilter);
|
|
30912
31411
|
return {
|
|
30913
|
-
...
|
|
30914
|
-
description: explicitFilter ? `${
|
|
31412
|
+
...tool100,
|
|
31413
|
+
description: explicitFilter ? `${tool100.description}
|
|
30915
31414
|
|
|
30916
|
-
Hive safety bound: ${explicitFilter} is required and accepts at most 100 entries; unbounded all-market reads are rejected.` :
|
|
31415
|
+
Hive safety bound: ${explicitFilter} is required and accepts at most 100 entries; unbounded all-market reads are rejected.` : tool100.description,
|
|
30917
31416
|
inputSchema: {
|
|
30918
31417
|
...schema,
|
|
30919
31418
|
type: "object",
|
|
@@ -30924,10 +31423,10 @@ Hive safety bound: ${explicitFilter} is required and accepts at most 100 entries
|
|
|
30924
31423
|
};
|
|
30925
31424
|
}
|
|
30926
31425
|
var tools = Object.fromEntries(
|
|
30927
|
-
Object.entries(rawTools).map(([key,
|
|
31426
|
+
Object.entries(rawTools).map(([key, tool100]) => [key, withCcxtBounds(tool100)])
|
|
30928
31427
|
);
|
|
30929
31428
|
var exportedHandlers = handlers2;
|
|
30930
|
-
var ccxt_default = { metadata:
|
|
31429
|
+
var ccxt_default = { metadata: metadata100, tools, handlers: exportedHandlers };
|
|
30931
31430
|
|
|
30932
31431
|
// src/tools/ccxt/index.ts
|
|
30933
31432
|
function getAllCCXTEndpoints() {
|
|
@@ -31768,7 +32267,7 @@ Use when:
|
|
|
31768
32267
|
- You need accurate gas estimation for a complex transaction
|
|
31769
32268
|
|
|
31770
32269
|
Don't use when:
|
|
31771
|
-
- You only need current token balances (use
|
|
32270
|
+
- You only need current token balances (use get_wallet_balances)
|
|
31772
32271
|
- You want historical transaction data (use moralis_get_wallet_history)
|
|
31773
32272
|
- You need token security analysis without simulation (use goplus_ tools)`,
|
|
31774
32273
|
inputSchema: {
|
|
@@ -31848,7 +32347,7 @@ function handleSimulateTransaction(clientParam, args) {
|
|
|
31848
32347
|
"Set TENDERLY_API_KEY, TENDERLY_ACCOUNT_SLUG, and TENDERLY_PROJECT_SLUG environment variables.",
|
|
31849
32348
|
[
|
|
31850
32349
|
"goplus_check_token_security \u2014 for token risk analysis without simulation",
|
|
31851
|
-
"
|
|
32350
|
+
"get_wallet_balances \u2014 for current wallet balances"
|
|
31852
32351
|
],
|
|
31853
32352
|
{
|
|
31854
32353
|
network_id: "1",
|
|
@@ -31887,7 +32386,7 @@ function handleSimulateTransaction(clientParam, args) {
|
|
|
31887
32386
|
"Check that network_id is valid, from/to are valid addresses, and input is properly hex-encoded.",
|
|
31888
32387
|
[
|
|
31889
32388
|
"goplus_check_token_security \u2014 for token risk analysis",
|
|
31890
|
-
"
|
|
32389
|
+
"get_wallet_balances \u2014 for current balances"
|
|
31891
32390
|
],
|
|
31892
32391
|
{
|
|
31893
32392
|
network_id: "1",
|
|
@@ -32428,7 +32927,7 @@ function handleGetBlockNumber(clientParam, args) {
|
|
|
32428
32927
|
);
|
|
32429
32928
|
}
|
|
32430
32929
|
function makeGatewayTool(key, name, method, description, properties, required, buildParams) {
|
|
32431
|
-
const
|
|
32930
|
+
const metadata104 = {
|
|
32432
32931
|
resource: `tenderly.gateway.${method}`,
|
|
32433
32932
|
operation: "read",
|
|
32434
32933
|
tags: ["blockchain", "evm", "tenderly", "gateway", "read"],
|
|
@@ -32436,7 +32935,7 @@ function makeGatewayTool(key, name, method, description, properties, required, b
|
|
|
32436
32935
|
httpPath: "/{network}.gateway.tenderly.co/{accessKey}",
|
|
32437
32936
|
operationId: `tenderly-${method.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}`
|
|
32438
32937
|
};
|
|
32439
|
-
const
|
|
32938
|
+
const tool100 = {
|
|
32440
32939
|
name,
|
|
32441
32940
|
title: name.replace(/^tenderly_/, "").replace(/_/g, " ").replace(/\b\w/g, (character) => character.toUpperCase()),
|
|
32442
32941
|
description: `${description}
|
|
@@ -32463,7 +32962,7 @@ Requires TENDERLY_NODE_ACCESS_KEY (or TENDERLY_GATEWAY_ACCESS_KEY). This is a re
|
|
|
32463
32962
|
openWorldHint: true
|
|
32464
32963
|
}
|
|
32465
32964
|
};
|
|
32466
|
-
const
|
|
32965
|
+
const handler100 = async (clientParam, args = {}) => {
|
|
32467
32966
|
const client2 = resolveClient(clientParam);
|
|
32468
32967
|
if (!client2) {
|
|
32469
32968
|
return createErrorResponse3(
|
|
@@ -32493,7 +32992,7 @@ Requires TENDERLY_NODE_ACCESS_KEY (or TENDERLY_GATEWAY_ACCESS_KEY). This is a re
|
|
|
32493
32992
|
);
|
|
32494
32993
|
}
|
|
32495
32994
|
};
|
|
32496
|
-
return { key, tool:
|
|
32995
|
+
return { key, tool: tool100, metadata: metadata104, handler: handler100 };
|
|
32497
32996
|
}
|
|
32498
32997
|
var accessListProp = {
|
|
32499
32998
|
type: "array",
|
|
@@ -32900,7 +33399,7 @@ var gatewayDefinitions = [
|
|
|
32900
33399
|
}
|
|
32901
33400
|
)
|
|
32902
33401
|
];
|
|
32903
|
-
var
|
|
33402
|
+
var metadata101 = {
|
|
32904
33403
|
simulateTransaction: simulateTransactionMetadata2,
|
|
32905
33404
|
simulateBundle: simulateBundleMetadata,
|
|
32906
33405
|
estimateGas: estimateGasMetadata,
|
|
@@ -32927,7 +33426,7 @@ var handlers3 = {
|
|
|
32927
33426
|
getBlockNumber: handleGetBlockNumber,
|
|
32928
33427
|
...Object.fromEntries(gatewayDefinitions.map((definition) => [definition.key, definition.handler]))
|
|
32929
33428
|
};
|
|
32930
|
-
var tenderly_default = { metadata:
|
|
33429
|
+
var tenderly_default = { metadata: metadata101, tools: tools2, handlers: handlers3 };
|
|
32931
33430
|
|
|
32932
33431
|
// src/tools/tenderly/index.ts
|
|
32933
33432
|
function getAllTenderlyEndpoints() {
|
|
@@ -40320,7 +40819,7 @@ var newHandlers = {
|
|
|
40320
40819
|
}
|
|
40321
40820
|
}
|
|
40322
40821
|
};
|
|
40323
|
-
function currentMoralisDefinition(key, name, resource, httpMethod, httpPath, description, properties, required,
|
|
40822
|
+
function currentMoralisDefinition(key, name, resource, httpMethod, httpPath, description, properties, required, handler100) {
|
|
40324
40823
|
return {
|
|
40325
40824
|
key,
|
|
40326
40825
|
metadata: {
|
|
@@ -40348,7 +40847,7 @@ function currentMoralisDefinition(key, name, resource, httpMethod, httpPath, des
|
|
|
40348
40847
|
openWorldHint: true
|
|
40349
40848
|
}
|
|
40350
40849
|
},
|
|
40351
|
-
handler:
|
|
40850
|
+
handler: handler100
|
|
40352
40851
|
};
|
|
40353
40852
|
}
|
|
40354
40853
|
var currentMoralisDefinitions = [
|
|
@@ -40577,7 +41076,7 @@ var currentMoralisTools = Object.fromEntries(
|
|
|
40577
41076
|
var currentMoralisHandlers = Object.fromEntries(
|
|
40578
41077
|
currentMoralisDefinitions.map((definition) => [definition.key, definition.handler])
|
|
40579
41078
|
);
|
|
40580
|
-
var
|
|
41079
|
+
var metadata102 = {
|
|
40581
41080
|
...currentMoralisMetadata,
|
|
40582
41081
|
getTokenScoreTimeseries: getTokenScoreTimeseriesMeta,
|
|
40583
41082
|
getTokenAnalyticsTimeseries: getTokenAnalyticsTimeseriesMeta,
|
|
@@ -40847,8 +41346,8 @@ var MORALIS_ARRAY_LIMITS = {
|
|
|
40847
41346
|
getNativeBalancesBatch: { wallet_addresses: 25 },
|
|
40848
41347
|
getNftCollectionMetadataBatch: { addresses: 25 }
|
|
40849
41348
|
};
|
|
40850
|
-
function withMoralisBounds(key,
|
|
40851
|
-
const schema =
|
|
41349
|
+
function withMoralisBounds(key, tool100) {
|
|
41350
|
+
const schema = tool100.inputSchema;
|
|
40852
41351
|
const properties = Object.fromEntries(
|
|
40853
41352
|
Object.entries(schema.properties || {}).map(
|
|
40854
41353
|
([name, rawProperty]) => {
|
|
@@ -40867,7 +41366,7 @@ function withMoralisBounds(key, tool96) {
|
|
|
40867
41366
|
)
|
|
40868
41367
|
);
|
|
40869
41368
|
return {
|
|
40870
|
-
...
|
|
41369
|
+
...tool100,
|
|
40871
41370
|
inputSchema: {
|
|
40872
41371
|
...schema,
|
|
40873
41372
|
type: "object",
|
|
@@ -40877,7 +41376,7 @@ function withMoralisBounds(key, tool96) {
|
|
|
40877
41376
|
};
|
|
40878
41377
|
}
|
|
40879
41378
|
var activeMetadata = Object.fromEntries(
|
|
40880
|
-
activeMoralisKeys.map((key) => [key,
|
|
41379
|
+
activeMoralisKeys.map((key) => [key, metadata102[key]])
|
|
40881
41380
|
);
|
|
40882
41381
|
var activeTools = Object.fromEntries(
|
|
40883
41382
|
activeMoralisKeys.map((key) => [key, withMoralisBounds(String(key), tools3[key])])
|
|
@@ -41688,7 +42187,7 @@ Use when:
|
|
|
41688
42187
|
|
|
41689
42188
|
Don't use when:
|
|
41690
42189
|
- You need raw token account data only (use helius_get_token_balances \u2014 faster)
|
|
41691
|
-
- You need EVM wallet data (use
|
|
42190
|
+
- You need EVM wallet data (use get_wallet_balances or get_wallet_portfolio)`,
|
|
41692
42191
|
inputSchema: {
|
|
41693
42192
|
type: "object",
|
|
41694
42193
|
properties: {
|
|
@@ -44184,7 +44683,7 @@ function meta(resource, path, method) {
|
|
|
44184
44683
|
};
|
|
44185
44684
|
}
|
|
44186
44685
|
function dataPost(key, name, description, path, properties, bodyBuilder, required = [], schemaExtensions = {}) {
|
|
44187
|
-
const
|
|
44686
|
+
const tool100 = makeTool(
|
|
44188
44687
|
name,
|
|
44189
44688
|
description,
|
|
44190
44689
|
properties,
|
|
@@ -44194,7 +44693,7 @@ function dataPost(key, name, description, path, properties, bodyBuilder, require
|
|
|
44194
44693
|
);
|
|
44195
44694
|
return {
|
|
44196
44695
|
key,
|
|
44197
|
-
tool:
|
|
44696
|
+
tool: tool100,
|
|
44198
44697
|
metadata: meta(key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`), path, "post"),
|
|
44199
44698
|
handler: async (clientParam, args = {}) => {
|
|
44200
44699
|
const client2 = resolveClient4(clientParam);
|
|
@@ -44209,7 +44708,7 @@ function dataPost(key, name, description, path, properties, bodyBuilder, require
|
|
|
44209
44708
|
};
|
|
44210
44709
|
}
|
|
44211
44710
|
function dataGet(key, name, description, path, properties, queryBuilder, required = [], schemaExtensions = {}) {
|
|
44212
|
-
const
|
|
44711
|
+
const tool100 = makeTool(
|
|
44213
44712
|
name,
|
|
44214
44713
|
description,
|
|
44215
44714
|
properties,
|
|
@@ -44219,7 +44718,7 @@ function dataGet(key, name, description, path, properties, queryBuilder, require
|
|
|
44219
44718
|
);
|
|
44220
44719
|
return {
|
|
44221
44720
|
key,
|
|
44222
|
-
tool:
|
|
44721
|
+
tool: tool100,
|
|
44223
44722
|
metadata: meta(key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`), path, "get"),
|
|
44224
44723
|
handler: async (clientParam, args = {}) => {
|
|
44225
44724
|
const client2 = resolveClient4(clientParam);
|
|
@@ -44234,7 +44733,7 @@ function dataGet(key, name, description, path, properties, queryBuilder, require
|
|
|
44234
44733
|
};
|
|
44235
44734
|
}
|
|
44236
44735
|
function nftGet(key, name, description, path, properties, queryBuilder, required = [], annotations = volatileAnnotations3, schemaExtensions = {}) {
|
|
44237
|
-
const
|
|
44736
|
+
const tool100 = makeTool(
|
|
44238
44737
|
name,
|
|
44239
44738
|
description,
|
|
44240
44739
|
properties,
|
|
@@ -44244,7 +44743,7 @@ function nftGet(key, name, description, path, properties, queryBuilder, required
|
|
|
44244
44743
|
);
|
|
44245
44744
|
return {
|
|
44246
44745
|
key,
|
|
44247
|
-
tool:
|
|
44746
|
+
tool: tool100,
|
|
44248
44747
|
metadata: meta(`nft.${key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`)}`, path, "get"),
|
|
44249
44748
|
handler: async (clientParam, args = {}) => {
|
|
44250
44749
|
const client2 = resolveClient4(clientParam);
|
|
@@ -44264,7 +44763,7 @@ function nftGet(key, name, description, path, properties, queryBuilder, required
|
|
|
44264
44763
|
};
|
|
44265
44764
|
}
|
|
44266
44765
|
function nftPost(key, name, description, path, properties, bodyBuilder, required = [], annotations = volatileAnnotations3, schemaExtensions = {}) {
|
|
44267
|
-
const
|
|
44766
|
+
const tool100 = makeTool(
|
|
44268
44767
|
name,
|
|
44269
44768
|
description,
|
|
44270
44769
|
properties,
|
|
@@ -44274,7 +44773,7 @@ function nftPost(key, name, description, path, properties, bodyBuilder, required
|
|
|
44274
44773
|
);
|
|
44275
44774
|
return {
|
|
44276
44775
|
key,
|
|
44277
|
-
tool:
|
|
44776
|
+
tool: tool100,
|
|
44278
44777
|
metadata: meta(`nft.${key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`)}`, path, "post"),
|
|
44279
44778
|
handler: async (clientParam, args = {}) => {
|
|
44280
44779
|
const client2 = resolveClient4(clientParam);
|
|
@@ -44294,7 +44793,7 @@ function nftPost(key, name, description, path, properties, bodyBuilder, required
|
|
|
44294
44793
|
};
|
|
44295
44794
|
}
|
|
44296
44795
|
function rpcTool(key, name, description, method, properties, paramsBuilder, required = [], schemaExtensions = {}, defaultNetwork = "eth-mainnet") {
|
|
44297
|
-
const
|
|
44796
|
+
const tool100 = makeTool(
|
|
44298
44797
|
name,
|
|
44299
44798
|
description,
|
|
44300
44799
|
properties,
|
|
@@ -44304,7 +44803,7 @@ function rpcTool(key, name, description, method, properties, paramsBuilder, requ
|
|
|
44304
44803
|
);
|
|
44305
44804
|
return {
|
|
44306
44805
|
key,
|
|
44307
|
-
tool:
|
|
44806
|
+
tool: tool100,
|
|
44308
44807
|
metadata: meta(`rpc.${key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`)}`, "/v2/{apiKey}", "post"),
|
|
44309
44808
|
handler: async (clientParam, args = {}) => {
|
|
44310
44809
|
const client2 = resolveClient4(clientParam);
|
|
@@ -46078,8 +46577,8 @@ var ALCHEMY_ARRAY_LIMITS = {
|
|
|
46078
46577
|
networks: 20,
|
|
46079
46578
|
category: 10
|
|
46080
46579
|
};
|
|
46081
|
-
function withAlchemyBounds(
|
|
46082
|
-
const schema =
|
|
46580
|
+
function withAlchemyBounds(tool100) {
|
|
46581
|
+
const schema = tool100.inputSchema;
|
|
46083
46582
|
const properties = Object.fromEntries(
|
|
46084
46583
|
Object.entries(schema.properties || {}).map(
|
|
46085
46584
|
([name, rawProperty]) => {
|
|
@@ -46101,20 +46600,20 @@ function withAlchemyBounds(tool96) {
|
|
|
46101
46600
|
}
|
|
46102
46601
|
)
|
|
46103
46602
|
);
|
|
46104
|
-
return { ...
|
|
46603
|
+
return { ...tool100, inputSchema: { ...schema, properties } };
|
|
46105
46604
|
}
|
|
46106
46605
|
var activeDefinitions = definitions.map((definition) => ({
|
|
46107
46606
|
...definition,
|
|
46108
46607
|
tool: withAlchemyBounds(definition.tool)
|
|
46109
46608
|
}));
|
|
46110
|
-
var
|
|
46609
|
+
var metadata103 = Object.fromEntries(
|
|
46111
46610
|
activeDefinitions.map((d) => [d.key, d.metadata])
|
|
46112
46611
|
);
|
|
46113
46612
|
var tools4 = Object.fromEntries(activeDefinitions.map((d) => [d.key, d.tool]));
|
|
46114
46613
|
var handlers5 = Object.fromEntries(
|
|
46115
46614
|
activeDefinitions.map((d) => [d.key, d.handler])
|
|
46116
46615
|
);
|
|
46117
|
-
var alchemy_default = { metadata:
|
|
46616
|
+
var alchemy_default = { metadata: metadata103, tools: tools4, handlers: handlers5 };
|
|
46118
46617
|
|
|
46119
46618
|
// src/tools/alchemy/index.ts
|
|
46120
46619
|
function getAllAlchemyEndpoints() {
|
|
@@ -46134,7 +46633,7 @@ function getAllAlchemyEndpoints() {
|
|
|
46134
46633
|
import axios3 from "axios";
|
|
46135
46634
|
var HYPERLIQUID_API_URL = "https://api.hyperliquid.xyz";
|
|
46136
46635
|
var DEFAULT_LEADERBOARD_LIMIT = 20;
|
|
46137
|
-
var MAX_LEADERBOARD_LIMIT =
|
|
46636
|
+
var MAX_LEADERBOARD_LIMIT = 500;
|
|
46138
46637
|
var FUNDING_HISTORY_PAGE_SIZE = 500;
|
|
46139
46638
|
var DEFAULT_FUNDING_HISTORY_RETURN_POINTS = 500;
|
|
46140
46639
|
var MAX_FUNDING_HISTORY_RETURN_POINTS = 500;
|
|
@@ -46177,7 +46676,7 @@ var HyperliquidAPI = class {
|
|
|
46177
46676
|
* Omit (or pass empty) for the main Hyperliquid DEX.
|
|
46178
46677
|
* @param limit - top N pairs by 24h notional volume (1-100, default 20).
|
|
46179
46678
|
*/
|
|
46180
|
-
async getPerpVolumeLeaderboard(dex, limit = DEFAULT_LEADERBOARD_LIMIT) {
|
|
46679
|
+
async getPerpVolumeLeaderboard(dex, limit = DEFAULT_LEADERBOARD_LIMIT, offset = 0) {
|
|
46181
46680
|
const body = { type: "metaAndAssetCtxs" };
|
|
46182
46681
|
if (dex) body.dex = dex;
|
|
46183
46682
|
const { data } = await this.client.post("/info", body);
|
|
@@ -46202,6 +46701,7 @@ var HyperliquidAPI = class {
|
|
|
46202
46701
|
Math.max(1, Math.floor(limit || DEFAULT_LEADERBOARD_LIMIT)),
|
|
46203
46702
|
MAX_LEADERBOARD_LIMIT
|
|
46204
46703
|
);
|
|
46704
|
+
const safeOffset = Math.max(0, Math.floor(offset || 0));
|
|
46205
46705
|
return {
|
|
46206
46706
|
dex: dex || "main",
|
|
46207
46707
|
pairCount: rows.length,
|
|
@@ -46209,7 +46709,8 @@ var HyperliquidAPI = class {
|
|
|
46209
46709
|
(sum, row) => sum + row.dayNotionalVolume,
|
|
46210
46710
|
0
|
|
46211
46711
|
),
|
|
46212
|
-
|
|
46712
|
+
offset: safeOffset,
|
|
46713
|
+
leaderboard: rows.slice(safeOffset, safeOffset + safeLimit)
|
|
46213
46714
|
};
|
|
46214
46715
|
}
|
|
46215
46716
|
/**
|
|
@@ -46438,6 +46939,85 @@ var HyperliquidAPI = class {
|
|
|
46438
46939
|
}
|
|
46439
46940
|
};
|
|
46440
46941
|
|
|
46942
|
+
// src/config/responseLimits.ts
|
|
46943
|
+
var RESPONSE_LIMITS = {
|
|
46944
|
+
// Maximum tokens allowed in a response (MCP constraint)
|
|
46945
|
+
MAX_RESPONSE_TOKENS: 25e3,
|
|
46946
|
+
// Estimated characters per token (rough approximation)
|
|
46947
|
+
CHARS_PER_TOKEN: 4,
|
|
46948
|
+
// Maximum characters allowed (25000 * 4 = 100,000)
|
|
46949
|
+
MAX_RESPONSE_CHARS: 1e5,
|
|
46950
|
+
// Default pagination limits for different endpoints
|
|
46951
|
+
DEFAULT_LIMITS: {
|
|
46952
|
+
tokens: 100,
|
|
46953
|
+
pools: 50,
|
|
46954
|
+
trades: 100,
|
|
46955
|
+
holders: 100,
|
|
46956
|
+
events: 200,
|
|
46957
|
+
nfts: 50
|
|
46958
|
+
},
|
|
46959
|
+
// Maximum allowed limits to prevent oversized responses
|
|
46960
|
+
MAX_LIMITS: {
|
|
46961
|
+
tokens: 100,
|
|
46962
|
+
pools: 50,
|
|
46963
|
+
trades: 200,
|
|
46964
|
+
holders: 200,
|
|
46965
|
+
events: 300,
|
|
46966
|
+
nfts: 100
|
|
46967
|
+
},
|
|
46968
|
+
// Size thresholds for automatic truncation (in characters)
|
|
46969
|
+
TRUNCATION_THRESHOLDS: {
|
|
46970
|
+
warning: 8e4,
|
|
46971
|
+
// Start warning at 80KB
|
|
46972
|
+
truncate: 1e5
|
|
46973
|
+
// Force truncation at 100KB
|
|
46974
|
+
}
|
|
46975
|
+
};
|
|
46976
|
+
|
|
46977
|
+
// src/utils/responseHeadroom.ts
|
|
46978
|
+
var MAX_RESULT_SIZE_META_KEY = "anthropic/maxResultSizeChars";
|
|
46979
|
+
var ANNOTATED_MAX_RESULT_SIZE_CHARS = RESPONSE_LIMITS.MAX_RESPONSE_CHARS;
|
|
46980
|
+
var DEFAULT_RESULT_SIZE_GUARD_CHARS = 2e4;
|
|
46981
|
+
var HEADROOM_TOOL_PATTERNS = [
|
|
46982
|
+
// Depth-of-book: order books, l2/l3 books.
|
|
46983
|
+
/orderbook|order_book|l2_book|l3_orderbook/i,
|
|
46984
|
+
// Holder / owner lists.
|
|
46985
|
+
/holders?(?:_|$)|owners_for/i,
|
|
46986
|
+
// OHLCV, candles, charts, and history/time-series families.
|
|
46987
|
+
/ohlc|candle|kline|market_chart|chart|history|timeseries/i
|
|
46988
|
+
];
|
|
46989
|
+
function toolNeedsResponseHeadroom(toolName) {
|
|
46990
|
+
return HEADROOM_TOOL_PATTERNS.some((pattern) => pattern.test(toolName));
|
|
46991
|
+
}
|
|
46992
|
+
function getAnnotatedMaxResultSizeChars(tool100) {
|
|
46993
|
+
const meta2 = tool100?._meta;
|
|
46994
|
+
const value = meta2?.[MAX_RESULT_SIZE_META_KEY];
|
|
46995
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
46996
|
+
}
|
|
46997
|
+
function withResponseHeadroomMeta(tool100) {
|
|
46998
|
+
if (!toolNeedsResponseHeadroom(tool100.name)) return tool100;
|
|
46999
|
+
const existing = tool100._meta;
|
|
47000
|
+
if (existing && existing[MAX_RESULT_SIZE_META_KEY] !== void 0) return tool100;
|
|
47001
|
+
return {
|
|
47002
|
+
...tool100,
|
|
47003
|
+
_meta: {
|
|
47004
|
+
...existing ?? {},
|
|
47005
|
+
[MAX_RESULT_SIZE_META_KEY]: ANNOTATED_MAX_RESULT_SIZE_CHARS
|
|
47006
|
+
}
|
|
47007
|
+
};
|
|
47008
|
+
}
|
|
47009
|
+
function largestFittingRowCount(max, fits) {
|
|
47010
|
+
if (max <= 0 || fits(max)) return Math.max(0, max);
|
|
47011
|
+
let lo = 1;
|
|
47012
|
+
let hi = max - 1;
|
|
47013
|
+
while (lo < hi) {
|
|
47014
|
+
const mid = Math.ceil((lo + hi) / 2);
|
|
47015
|
+
if (fits(mid)) lo = mid;
|
|
47016
|
+
else hi = mid - 1;
|
|
47017
|
+
}
|
|
47018
|
+
return lo;
|
|
47019
|
+
}
|
|
47020
|
+
|
|
46441
47021
|
// src/tools/hyperliquid/infoTools.ts
|
|
46442
47022
|
var MAX_ARRAY_ITEMS = 500;
|
|
46443
47023
|
var SPOT_TOKEN_TTL_MS = 10 * 60 * 1e3;
|
|
@@ -47376,14 +47956,19 @@ Hyperliquid is the dominant on-chain perpetual DEX. This reads the venue's own p
|
|
|
47376
47956
|
|
|
47377
47957
|
Args:
|
|
47378
47958
|
- dex (string, optional): Builder DEX name to scope the leaderboard to (e.g. "xyz" for tokenized-stock / pre-IPO perps like SPCX, MU, XYZ100). Omit for Hyperliquid's main perp DEX. Call hyperliquid_get_perp_dexs to discover builder DEX names.
|
|
47379
|
-
- limit (number, optional): Number of top pairs to return, ranked by 24h notional volume (1
|
|
47959
|
+
- limit (number, optional): Number of top pairs to return, ranked by 24h notional volume (1-${MAX_LEADERBOARD_LIMIT}, default 20). A page also stops at the ~${DEFAULT_RESULT_SIZE_GUARD_CHARS / 1e3}k-character response budget (about 100 pairs); page on with offset=next_offset to read every pair.
|
|
47960
|
+
- offset (number, optional): Rank to start from (0 = top pair, default 0).
|
|
47380
47961
|
|
|
47381
47962
|
Returns:
|
|
47382
47963
|
JSON object:
|
|
47383
47964
|
{
|
|
47384
47965
|
"dex": string, // "main" or the builder DEX name
|
|
47385
47966
|
"pairCount": number, // total pairs on this DEX
|
|
47386
|
-
"totalDayNotionalVolume": number, // summed 24h notional volume (USD)
|
|
47967
|
+
"totalDayNotionalVolume": number, // summed 24h notional volume over ALL pairs (USD), not just this page
|
|
47968
|
+
"offset": number, // rank of the first row
|
|
47969
|
+
"returnedCount": number, // rows in this response
|
|
47970
|
+
"next_offset": number | null, // pass as offset for the next page; null after the last pair
|
|
47971
|
+
"pagination_note"?: string, // present when the response budget cut the requested page short
|
|
47387
47972
|
"leaderboard": [{
|
|
47388
47973
|
"coin": string, // perp symbol (e.g. "BTC", "HYPE", "SPCX")
|
|
47389
47974
|
"dayNotionalVolume": number, // 24h notional volume (USD)
|
|
@@ -47414,8 +47999,13 @@ Don't use when:
|
|
|
47414
47999
|
limit: {
|
|
47415
48000
|
type: "integer",
|
|
47416
48001
|
minimum: 1,
|
|
47417
|
-
maximum:
|
|
47418
|
-
description:
|
|
48002
|
+
maximum: MAX_LEADERBOARD_LIMIT,
|
|
48003
|
+
description: `Top N pairs by 24h notional volume (1-${MAX_LEADERBOARD_LIMIT}, default 20). One page carries about 100 pairs (response budget); returnedCount and next_offset say what shipped.`
|
|
48004
|
+
},
|
|
48005
|
+
offset: {
|
|
48006
|
+
type: "integer",
|
|
48007
|
+
minimum: 0,
|
|
48008
|
+
description: "Rank to start from (0 = top pair). Pass the previous response's next_offset for the next page."
|
|
47419
48009
|
}
|
|
47420
48010
|
}
|
|
47421
48011
|
},
|
|
@@ -47424,8 +48014,29 @@ Don't use when:
|
|
|
47424
48014
|
handler: async (_client, args) => {
|
|
47425
48015
|
const dex = typeof args?.dex === "string" ? args.dex : void 0;
|
|
47426
48016
|
const limit = typeof args?.limit === "number" ? args.limit : void 0;
|
|
47427
|
-
const
|
|
47428
|
-
|
|
48017
|
+
const offset = typeof args?.offset === "number" ? args.offset : 0;
|
|
48018
|
+
const data = await api.getPerpVolumeLeaderboard(dex, limit, offset);
|
|
48019
|
+
const requested = data.leaderboard;
|
|
48020
|
+
const page = (kept) => {
|
|
48021
|
+
const next = data.offset + kept;
|
|
48022
|
+
return {
|
|
48023
|
+
...data,
|
|
48024
|
+
returnedCount: kept,
|
|
48025
|
+
next_offset: next < data.pairCount ? next : null,
|
|
48026
|
+
...kept < requested.length && {
|
|
48027
|
+
pagination_note: `${kept} of the ${requested.length} requested pairs fit the response budget. Re-call with offset=${next} for the next page.`
|
|
48028
|
+
},
|
|
48029
|
+
leaderboard: requested.slice(0, kept)
|
|
48030
|
+
};
|
|
48031
|
+
};
|
|
48032
|
+
return asTextContentResult(
|
|
48033
|
+
page(
|
|
48034
|
+
largestFittingRowCount(
|
|
48035
|
+
requested.length,
|
|
48036
|
+
(kept) => JSON.stringify(page(kept)).length <= DEFAULT_RESULT_SIZE_GUARD_CHARS - 500
|
|
48037
|
+
)
|
|
48038
|
+
)
|
|
48039
|
+
);
|
|
47429
48040
|
}
|
|
47430
48041
|
},
|
|
47431
48042
|
{
|
|
@@ -48599,6 +49210,7 @@ async function collectRwaRows(deps, venues = RWA_VENUE_IDS) {
|
|
|
48599
49210
|
// src/tools/rwa/tools.ts
|
|
48600
49211
|
var DEFAULT_LIMIT = 100;
|
|
48601
49212
|
var MAX_LIMIT = 500;
|
|
49213
|
+
var PAGE_BUDGET_CHARS = DEFAULT_RESULT_SIZE_GUARD_CHARS - 500;
|
|
48602
49214
|
var ASSET_CLASSES = [
|
|
48603
49215
|
"equity",
|
|
48604
49216
|
"pre_ipo",
|
|
@@ -48620,6 +49232,10 @@ function clampLimit2(value) {
|
|
|
48620
49232
|
if (!Number.isFinite(parsed)) return DEFAULT_LIMIT;
|
|
48621
49233
|
return Math.min(Math.max(parsed, 1), MAX_LIMIT);
|
|
48622
49234
|
}
|
|
49235
|
+
function clampOffset(value) {
|
|
49236
|
+
const parsed = typeof value === "number" ? Math.floor(value) : NaN;
|
|
49237
|
+
return Number.isFinite(parsed) ? Math.max(parsed, 0) : 0;
|
|
49238
|
+
}
|
|
48623
49239
|
function sortRows(rows) {
|
|
48624
49240
|
return [...rows].sort((a, b) => {
|
|
48625
49241
|
const oiA = a.open_interest_usd ?? -1;
|
|
@@ -48657,15 +49273,30 @@ function applyFilters(rows, args) {
|
|
|
48657
49273
|
}
|
|
48658
49274
|
};
|
|
48659
49275
|
}
|
|
48660
|
-
function
|
|
49276
|
+
function boundedPayload(rows, args, render) {
|
|
48661
49277
|
const sorted = sortRows(rows);
|
|
48662
|
-
const
|
|
48663
|
-
|
|
48664
|
-
|
|
48665
|
-
|
|
48666
|
-
|
|
48667
|
-
|
|
49278
|
+
const offset = clampOffset(args?.offset);
|
|
49279
|
+
const requested = sorted.slice(offset, offset + clampLimit2(args?.limit));
|
|
49280
|
+
const build = (kept) => {
|
|
49281
|
+
const next = offset + kept;
|
|
49282
|
+
const page = {
|
|
49283
|
+
count: sorted.length,
|
|
49284
|
+
offset,
|
|
49285
|
+
returnedCount: kept,
|
|
49286
|
+
truncated: next < sorted.length,
|
|
49287
|
+
next_offset: next < sorted.length ? next : null,
|
|
49288
|
+
...kept < requested.length && {
|
|
49289
|
+
pagination_note: `${kept} of the ${requested.length} requested rows fit the response budget. Re-call with offset=${next} for the next page.`
|
|
49290
|
+
}
|
|
49291
|
+
};
|
|
49292
|
+
return render(page, requested.slice(0, kept));
|
|
48668
49293
|
};
|
|
49294
|
+
return build(
|
|
49295
|
+
largestFittingRowCount(
|
|
49296
|
+
requested.length,
|
|
49297
|
+
(kept) => JSON.stringify(build(kept)).length <= PAGE_BUDGET_CHARS
|
|
49298
|
+
)
|
|
49299
|
+
);
|
|
48669
49300
|
}
|
|
48670
49301
|
var FILTER_PARAM_PROPERTIES = {
|
|
48671
49302
|
underlying: {
|
|
@@ -48685,7 +49316,12 @@ var FILTER_PARAM_PROPERTIES = {
|
|
|
48685
49316
|
type: "integer",
|
|
48686
49317
|
minimum: 1,
|
|
48687
49318
|
maximum: MAX_LIMIT,
|
|
48688
|
-
description: `Maximum rows to return (1-${MAX_LIMIT}, default ${DEFAULT_LIMIT}). Rows are ordered by open interest (deepest first).`
|
|
49319
|
+
description: `Maximum rows to return (1-${MAX_LIMIT}, default ${DEFAULT_LIMIT}). Rows are ordered by open interest (deepest first). A page also stops at the ~${DEFAULT_RESULT_SIZE_GUARD_CHARS / 1e3}k-character response budget (about 30 full rows); returnedCount is what shipped and next_offset continues.`
|
|
49320
|
+
},
|
|
49321
|
+
offset: {
|
|
49322
|
+
type: "integer",
|
|
49323
|
+
minimum: 0,
|
|
49324
|
+
description: "Rows to skip in the open-interest order (default 0). Pass the previous response's next_offset to read the next page."
|
|
48689
49325
|
}
|
|
48690
49326
|
};
|
|
48691
49327
|
function toMarketRow(row) {
|
|
@@ -48733,9 +49369,10 @@ Args:
|
|
|
48733
49369
|
- underlying (string, optional): filter to one canonical underlying ticker.
|
|
48734
49370
|
- asset_class (string, optional): filter to one asset class.
|
|
48735
49371
|
- limit (integer, optional): max rows (1-${MAX_LIMIT}, default ${DEFAULT_LIMIT}), deepest open interest first.
|
|
49372
|
+
- offset (integer, optional): rows to skip; pass next_offset to page.
|
|
48736
49373
|
|
|
48737
49374
|
Returns:
|
|
48738
|
-
Bounded JSON envelope: { "venue", "count", "returnedCount", "truncated", "rows": [normalized rows] }. ${returnsNote}
|
|
49375
|
+
Bounded JSON envelope: { "venue", "count", "offset", "returnedCount", "truncated", "next_offset", "pagination_note"?, "rows": [normalized rows] }. A page holds at most what fits the response budget (about 30 rows): returnedCount is the rows shipped, next_offset is null on the last page. ${returnsNote}
|
|
48739
49376
|
|
|
48740
49377
|
Use when:
|
|
48741
49378
|
${useWhen.map((line) => ` - ${line}`).join("\n")}
|
|
@@ -48747,7 +49384,8 @@ ${dontUseWhen.map((line) => ` - ${line}`).join("\n")}`,
|
|
|
48747
49384
|
properties: {
|
|
48748
49385
|
underlying: FILTER_PARAM_PROPERTIES.underlying,
|
|
48749
49386
|
asset_class: FILTER_PARAM_PROPERTIES.asset_class,
|
|
48750
|
-
limit: FILTER_PARAM_PROPERTIES.limit
|
|
49387
|
+
limit: FILTER_PARAM_PROPERTIES.limit,
|
|
49388
|
+
offset: FILTER_PARAM_PROPERTIES.offset
|
|
48751
49389
|
}
|
|
48752
49390
|
},
|
|
48753
49391
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
@@ -48756,13 +49394,15 @@ ${dontUseWhen.map((line) => ` - ${line}`).join("\n")}`,
|
|
|
48756
49394
|
const { rows, note } = await fetchRwaVenueRows(venueId, deps);
|
|
48757
49395
|
stampPriceSuspect(rows);
|
|
48758
49396
|
const { filtered, filters } = applyFilters(rows, args);
|
|
48759
|
-
|
|
48760
|
-
|
|
48761
|
-
|
|
48762
|
-
|
|
48763
|
-
|
|
48764
|
-
|
|
48765
|
-
|
|
49397
|
+
return asTextContentResult(
|
|
49398
|
+
boundedPayload(filtered, args, (page, pageRows) => ({
|
|
49399
|
+
venue: venueId,
|
|
49400
|
+
...note ? { note } : {},
|
|
49401
|
+
filters,
|
|
49402
|
+
...page,
|
|
49403
|
+
rows: pageRows
|
|
49404
|
+
}))
|
|
49405
|
+
);
|
|
48766
49406
|
}
|
|
48767
49407
|
};
|
|
48768
49408
|
}
|
|
@@ -48798,6 +49438,7 @@ Args:
|
|
|
48798
49438
|
- venue (string, optional): filter to one venue ("ostium", "avantis", "lighter", "extended", "hyperliquid", or "hyperliquid:xyz").
|
|
48799
49439
|
- asset_class (string, optional): filter to one asset class (equity, pre_ipo, etf, index, commodity, fx, crypto).
|
|
48800
49440
|
- limit (integer, optional): max rows (1-${MAX_LIMIT}, default ${DEFAULT_LIMIT}), deepest open interest first.
|
|
49441
|
+
- offset (integer, optional): rows to skip; pass next_offset to page.
|
|
48801
49442
|
|
|
48802
49443
|
Returns:
|
|
48803
49444
|
JSON object:
|
|
@@ -48805,9 +49446,11 @@ Returns:
|
|
|
48805
49446
|
"filters": { "underlying", "venue", "asset_class" },
|
|
48806
49447
|
"venueCount": number, "okCount": number,
|
|
48807
49448
|
"venues": [{ "venue", "runtime_status", "rowCount", "error"? }],
|
|
48808
|
-
"count": number, "returnedCount": number, "truncated": boolean,
|
|
49449
|
+
"count": number, "offset": number, "returnedCount": number, "truncated": boolean,
|
|
49450
|
+
"next_offset": number | null, "pagination_note"?: string,
|
|
48809
49451
|
"rows": [normalized funding rows]
|
|
48810
49452
|
}
|
|
49453
|
+
A page holds at most what fits the response budget (about 30 rows): returnedCount is the rows shipped; re-call with offset=next_offset for more.
|
|
48811
49454
|
|
|
48812
49455
|
Use when:
|
|
48813
49456
|
- "TSLA perp funding across venues" / tokenized-stock or equity perp funding questions
|
|
@@ -48827,17 +49470,19 @@ Don't use when:
|
|
|
48827
49470
|
handler: async (_client, args) => {
|
|
48828
49471
|
const { results, rows } = await collectRwaRows(deps);
|
|
48829
49472
|
const { filtered, filters } = applyFilters(rows, args);
|
|
48830
|
-
const envelope = boundedEnvelope(filtered, clampLimit2(args?.limit));
|
|
48831
49473
|
const okCount = results.filter(
|
|
48832
49474
|
(result2) => result2.runtime_status === "ok"
|
|
48833
49475
|
).length;
|
|
48834
|
-
return asTextContentResult(
|
|
48835
|
-
|
|
48836
|
-
|
|
48837
|
-
|
|
48838
|
-
|
|
48839
|
-
|
|
48840
|
-
|
|
49476
|
+
return asTextContentResult(
|
|
49477
|
+
boundedPayload(filtered, args, (page, pageRows) => ({
|
|
49478
|
+
filters,
|
|
49479
|
+
venueCount: results.length,
|
|
49480
|
+
okCount,
|
|
49481
|
+
venues: results,
|
|
49482
|
+
...page,
|
|
49483
|
+
rows: pageRows
|
|
49484
|
+
}))
|
|
49485
|
+
);
|
|
48841
49486
|
}
|
|
48842
49487
|
},
|
|
48843
49488
|
{
|
|
@@ -48864,6 +49509,7 @@ Args:
|
|
|
48864
49509
|
- venue (string, optional): filter to one venue.
|
|
48865
49510
|
- asset_class (string, optional): filter to one asset class (equity, pre_ipo, etf, index, commodity, fx, crypto).
|
|
48866
49511
|
- limit (integer, optional): max rows (1-${MAX_LIMIT}, default ${DEFAULT_LIMIT}), deepest open interest first.
|
|
49512
|
+
- offset (integer, optional): rows to skip; pass next_offset to page.
|
|
48867
49513
|
|
|
48868
49514
|
Returns:
|
|
48869
49515
|
JSON object:
|
|
@@ -48871,7 +49517,7 @@ Returns:
|
|
|
48871
49517
|
"filters": {...},
|
|
48872
49518
|
"venueCount": number, "okCount": number,
|
|
48873
49519
|
"venues": [{ "venue", "runtime_status", "rowCount", "error"? }],
|
|
48874
|
-
"count", "returnedCount", "truncated",
|
|
49520
|
+
"count", "offset", "returnedCount", "truncated", "next_offset", "pagination_note"?,
|
|
48875
49521
|
"rows": [{ "venue", "market", "symbol", "underlying", "asset_class", "funding_mechanism", "is_delisted", "mark_price", "oracle_price", "open_interest_usd", "volume_24h_usd", "is_price_suspect", "price_suspect_basis", "price_deviation_pct", "meta" }]
|
|
48876
49522
|
}
|
|
48877
49523
|
|
|
@@ -48892,20 +49538,19 @@ Don't use when:
|
|
|
48892
49538
|
handler: async (_client, args) => {
|
|
48893
49539
|
const { results, rows } = await collectRwaRows(deps);
|
|
48894
49540
|
const { filtered, filters } = applyFilters(rows, args);
|
|
48895
|
-
const envelope = boundedEnvelope(filtered, clampLimit2(args?.limit));
|
|
48896
49541
|
const okCount = results.filter(
|
|
48897
49542
|
(result2) => result2.runtime_status === "ok"
|
|
48898
49543
|
).length;
|
|
48899
|
-
return asTextContentResult(
|
|
48900
|
-
|
|
48901
|
-
|
|
48902
|
-
|
|
48903
|
-
|
|
48904
|
-
|
|
48905
|
-
|
|
48906
|
-
|
|
48907
|
-
|
|
48908
|
-
|
|
49544
|
+
return asTextContentResult(
|
|
49545
|
+
boundedPayload(filtered, args, (page, pageRows) => ({
|
|
49546
|
+
filters,
|
|
49547
|
+
venueCount: results.length,
|
|
49548
|
+
okCount,
|
|
49549
|
+
venues: results,
|
|
49550
|
+
...page,
|
|
49551
|
+
rows: pageRows.map(toMarketRow)
|
|
49552
|
+
}))
|
|
49553
|
+
);
|
|
48909
49554
|
}
|
|
48910
49555
|
},
|
|
48911
49556
|
// -----------------------------------------------------------------
|
|
@@ -49285,13 +49930,13 @@ function historyInputSchema(baseCoinDescription) {
|
|
|
49285
49930
|
required: ["base_coin"]
|
|
49286
49931
|
};
|
|
49287
49932
|
}
|
|
49288
|
-
function errorResult2(
|
|
49933
|
+
function errorResult2(tool100, error) {
|
|
49289
49934
|
if (error instanceof ArchiveInputError) {
|
|
49290
49935
|
return asTextContentResult(
|
|
49291
49936
|
{
|
|
49292
49937
|
error: error.message,
|
|
49293
49938
|
runtime_status: "invalid_input",
|
|
49294
|
-
tool:
|
|
49939
|
+
tool: tool100
|
|
49295
49940
|
},
|
|
49296
49941
|
true
|
|
49297
49942
|
);
|
|
@@ -49301,17 +49946,17 @@ function errorResult2(tool96, error) {
|
|
|
49301
49946
|
{
|
|
49302
49947
|
error: message,
|
|
49303
49948
|
runtime_status: classifyRuntimeStatusFromError(message),
|
|
49304
|
-
tool:
|
|
49949
|
+
tool: tool100
|
|
49305
49950
|
},
|
|
49306
49951
|
true
|
|
49307
49952
|
);
|
|
49308
49953
|
}
|
|
49309
|
-
function missingCredentialsResult(
|
|
49954
|
+
function missingCredentialsResult(tool100) {
|
|
49310
49955
|
return asTextContentResult(
|
|
49311
49956
|
{
|
|
49312
49957
|
error: ARCHIVE_MISSING_CREDENTIALS_MESSAGE,
|
|
49313
49958
|
runtime_status: "missing_key",
|
|
49314
|
-
tool:
|
|
49959
|
+
tool: tool100
|
|
49315
49960
|
},
|
|
49316
49961
|
true
|
|
49317
49962
|
);
|
|
@@ -49685,74 +50330,6 @@ async function fetchPolymarketJson(url, label, timeoutMs = POLYMARKET_TIMEOUT_MS
|
|
|
49685
50330
|
}
|
|
49686
50331
|
}
|
|
49687
50332
|
|
|
49688
|
-
// src/config/responseLimits.ts
|
|
49689
|
-
var RESPONSE_LIMITS = {
|
|
49690
|
-
// Maximum tokens allowed in a response (MCP constraint)
|
|
49691
|
-
MAX_RESPONSE_TOKENS: 25e3,
|
|
49692
|
-
// Estimated characters per token (rough approximation)
|
|
49693
|
-
CHARS_PER_TOKEN: 4,
|
|
49694
|
-
// Maximum characters allowed (25000 * 4 = 100,000)
|
|
49695
|
-
MAX_RESPONSE_CHARS: 1e5,
|
|
49696
|
-
// Default pagination limits for different endpoints
|
|
49697
|
-
DEFAULT_LIMITS: {
|
|
49698
|
-
tokens: 100,
|
|
49699
|
-
pools: 50,
|
|
49700
|
-
trades: 100,
|
|
49701
|
-
holders: 100,
|
|
49702
|
-
events: 200,
|
|
49703
|
-
nfts: 50
|
|
49704
|
-
},
|
|
49705
|
-
// Maximum allowed limits to prevent oversized responses
|
|
49706
|
-
MAX_LIMITS: {
|
|
49707
|
-
tokens: 100,
|
|
49708
|
-
pools: 50,
|
|
49709
|
-
trades: 200,
|
|
49710
|
-
holders: 200,
|
|
49711
|
-
events: 300,
|
|
49712
|
-
nfts: 100
|
|
49713
|
-
},
|
|
49714
|
-
// Size thresholds for automatic truncation (in characters)
|
|
49715
|
-
TRUNCATION_THRESHOLDS: {
|
|
49716
|
-
warning: 8e4,
|
|
49717
|
-
// Start warning at 80KB
|
|
49718
|
-
truncate: 1e5
|
|
49719
|
-
// Force truncation at 100KB
|
|
49720
|
-
}
|
|
49721
|
-
};
|
|
49722
|
-
|
|
49723
|
-
// src/utils/responseHeadroom.ts
|
|
49724
|
-
var MAX_RESULT_SIZE_META_KEY = "anthropic/maxResultSizeChars";
|
|
49725
|
-
var ANNOTATED_MAX_RESULT_SIZE_CHARS = RESPONSE_LIMITS.MAX_RESPONSE_CHARS;
|
|
49726
|
-
var DEFAULT_RESULT_SIZE_GUARD_CHARS = 2e4;
|
|
49727
|
-
var HEADROOM_TOOL_PATTERNS = [
|
|
49728
|
-
// Depth-of-book: order books, l2/l3 books.
|
|
49729
|
-
/orderbook|order_book|l2_book|l3_orderbook/i,
|
|
49730
|
-
// Holder / owner lists.
|
|
49731
|
-
/holders?(?:_|$)|owners_for/i,
|
|
49732
|
-
// OHLCV, candles, charts, and history/time-series families.
|
|
49733
|
-
/ohlc|candle|kline|market_chart|chart|history|timeseries/i
|
|
49734
|
-
];
|
|
49735
|
-
function toolNeedsResponseHeadroom(toolName) {
|
|
49736
|
-
return HEADROOM_TOOL_PATTERNS.some((pattern) => pattern.test(toolName));
|
|
49737
|
-
}
|
|
49738
|
-
function getAnnotatedMaxResultSizeChars(tool96) {
|
|
49739
|
-
const meta2 = tool96?._meta;
|
|
49740
|
-
const value = meta2?.[MAX_RESULT_SIZE_META_KEY];
|
|
49741
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
49742
|
-
}
|
|
49743
|
-
function withResponseHeadroomMeta(tool96) {
|
|
49744
|
-
if (!toolNeedsResponseHeadroom(tool96.name)) return tool96;
|
|
49745
|
-
const existing = tool96._meta;
|
|
49746
|
-
if (existing && existing[MAX_RESULT_SIZE_META_KEY] !== void 0) return tool96;
|
|
49747
|
-
return {
|
|
49748
|
-
...tool96,
|
|
49749
|
-
_meta: {
|
|
49750
|
-
...existing ?? {},
|
|
49751
|
-
[MAX_RESULT_SIZE_META_KEY]: ANNOTATED_MAX_RESULT_SIZE_CHARS
|
|
49752
|
-
}
|
|
49753
|
-
};
|
|
49754
|
-
}
|
|
49755
|
-
|
|
49756
50333
|
// src/tools/polymarket/api.ts
|
|
49757
50334
|
var DEFAULT_LIMIT3 = 10;
|
|
49758
50335
|
var MAX_LIMIT3 = 50;
|
|
@@ -49760,9 +50337,14 @@ var MAX_OFFSET = 1e4;
|
|
|
49760
50337
|
var DEFAULT_BOOK_DEPTH = 10;
|
|
49761
50338
|
var MAX_BOOK_DEPTH = 50;
|
|
49762
50339
|
var MAX_HISTORY_POINTS = 500;
|
|
49763
|
-
var
|
|
50340
|
+
var SEARCH_EVENT_LIMIT = 20;
|
|
49764
50341
|
var EVENT_MARKETS_PREVIEW = 10;
|
|
49765
|
-
var EVENT_RESPONSE_BUDGET_CHARS =
|
|
50342
|
+
var EVENT_RESPONSE_BUDGET_CHARS = (
|
|
50343
|
+
// Rows only: the okResult envelope (odds note, source URL twice, filters)
|
|
50344
|
+
// adds ~1k, and at the full 20k the guard still trimmed 48 rows to 45
|
|
50345
|
+
// (order=volume, limit=50, 2026-10-09).
|
|
50346
|
+
DEFAULT_RESULT_SIZE_GUARD_CHARS - 2e3
|
|
50347
|
+
);
|
|
49766
50348
|
var DESCRIPTION_MAX_CHARS = 1200;
|
|
49767
50349
|
var PRICE_INTERVALS = ["1d", "1w", "1m", "max"];
|
|
49768
50350
|
var DEFAULT_FIDELITY_MINUTES = {
|
|
@@ -49962,6 +50544,21 @@ function parseInterval(value) {
|
|
|
49962
50544
|
}
|
|
49963
50545
|
return interval;
|
|
49964
50546
|
}
|
|
50547
|
+
var EVENT_ORDERS = [
|
|
50548
|
+
"volume24hr",
|
|
50549
|
+
"volume",
|
|
50550
|
+
"liquidity",
|
|
50551
|
+
"endDate"
|
|
50552
|
+
];
|
|
50553
|
+
function eventOrderParams(value, now = /* @__PURE__ */ new Date()) {
|
|
50554
|
+
const order = value === void 0 || value === null ? "volume24hr" : value;
|
|
50555
|
+
if (typeof order !== "string" || !EVENT_ORDERS.includes(order)) {
|
|
50556
|
+
invalid(
|
|
50557
|
+
`order must be one of ${EVENT_ORDERS.map((o) => `"${o}"`).join(", ")}; got ${JSON.stringify(value)}.`
|
|
50558
|
+
);
|
|
50559
|
+
}
|
|
50560
|
+
return order === "endDate" ? { order, ascending: true, end_date_min: now.toISOString() } : { order, ascending: false };
|
|
50561
|
+
}
|
|
49965
50562
|
function parseFidelity(value, interval) {
|
|
49966
50563
|
return parseBoundedInteger(
|
|
49967
50564
|
value,
|
|
@@ -50290,6 +50887,22 @@ async function fetchGammaEvents(params) {
|
|
|
50290
50887
|
const data = await fetchPolymarketJson(url, "Gamma /events");
|
|
50291
50888
|
return { url, rows: asRecordArray(data, "Gamma /events") };
|
|
50292
50889
|
}
|
|
50890
|
+
async function searchGammaMarkets(query, closed) {
|
|
50891
|
+
const url = buildUrl(GAMMA_BASE_URL, "/public-search", {
|
|
50892
|
+
q: query,
|
|
50893
|
+
limit_per_type: SEARCH_EVENT_LIMIT,
|
|
50894
|
+
events_status: closed ? "closed" : "active"
|
|
50895
|
+
});
|
|
50896
|
+
const data = asRecord(await fetchPolymarketJson(url, "Gamma /public-search"));
|
|
50897
|
+
const events = asRecordArray(data?.events ?? [], "Gamma /public-search");
|
|
50898
|
+
const rows = events.flatMap(
|
|
50899
|
+
(event) => (Array.isArray(event.markets) ? event.markets : []).map(asRecord).filter((market) => market !== null).map((market) => ({
|
|
50900
|
+
...market,
|
|
50901
|
+
events: [{ slug: event.slug, title: event.title }]
|
|
50902
|
+
}))
|
|
50903
|
+
);
|
|
50904
|
+
return { url, rows, eventCount: events.length };
|
|
50905
|
+
}
|
|
50293
50906
|
async function resolveMarket(selector) {
|
|
50294
50907
|
if (selector.slug) {
|
|
50295
50908
|
const { url, rows } = await fetchGammaMarkets({ slug: selector.slug });
|
|
@@ -50410,13 +51023,13 @@ function okResult(sourceUrl, body) {
|
|
|
50410
51023
|
may_have_more
|
|
50411
51024
|
});
|
|
50412
51025
|
}
|
|
50413
|
-
function errorResult3(
|
|
51026
|
+
function errorResult3(tool100, error) {
|
|
50414
51027
|
if (error instanceof PolymarketInputError) {
|
|
50415
51028
|
return asTextContentResult(
|
|
50416
51029
|
{
|
|
50417
51030
|
error: error.message,
|
|
50418
51031
|
runtime_status: "invalid_input",
|
|
50419
|
-
tool:
|
|
51032
|
+
tool: tool100
|
|
50420
51033
|
},
|
|
50421
51034
|
true
|
|
50422
51035
|
);
|
|
@@ -50426,7 +51039,7 @@ function errorResult3(tool96, error) {
|
|
|
50426
51039
|
{
|
|
50427
51040
|
error: message,
|
|
50428
51041
|
runtime_status: classifyRuntimeStatusFromError(message),
|
|
50429
|
-
tool:
|
|
51042
|
+
tool: tool100
|
|
50430
51043
|
},
|
|
50431
51044
|
true
|
|
50432
51045
|
);
|
|
@@ -50466,7 +51079,7 @@ function getAllPolymarketEndpoints() {
|
|
|
50466
51079
|
},
|
|
50467
51080
|
tool: {
|
|
50468
51081
|
name: "polymarket_search_markets",
|
|
50469
|
-
description: `Search Polymarket prediction markets by free text and list their market-implied odds, liquidity, volume, CLOB token ids, and end dates (Gamma API, keyless). With a query the tool
|
|
51082
|
+
description: `Search Polymarket prediction markets by free text and list their market-implied odds, liquidity, volume, CLOB token ids, and end dates (Gamma API, keyless). With a query the tool runs Polymarket's own search across every event (up to ${SEARCH_EVENT_LIMIT} events, honoring the active/closed filters), keeps markets where every query term appears in the question, slug, bracket title, or event title, and orders them by 24h volume; when no market contains every term it returns Polymarket's relevance order instead and says so in scan.order. Without a query it returns the top markets by 24h volume.
|
|
50470
51083
|
|
|
50471
51084
|
Args:
|
|
50472
51085
|
- query (string, optional): free-text terms, all must match (case-insensitive), e.g. "fed rate" or "bitcoin 2026".
|
|
@@ -50477,7 +51090,7 @@ Args:
|
|
|
50477
51090
|
Returns:
|
|
50478
51091
|
${ENVELOPE_DOC}
|
|
50479
51092
|
"filters", "scan" { "scanned", "scan_limit", "matched", "order" }, and "rows": [{ "id", "slug", "question", "groupItemTitle", "conditionId", "eventSlug", "eventTitle", "outcomes", "outcomePrices" (0-1 per outcome, same order as outcomes), "clobTokenIds" (same order; pass to polymarket_get_orderbook / polymarket_get_price_history), "liquidity", "volume", "volume24hr", "endDate", "active", "closed" }].
|
|
50480
|
-
may_have_more is true when more rows matched than were returned or the
|
|
51093
|
+
scan.scan_limit is the event cap for a query and the row limit without one. may_have_more is true when more rows matched than were returned or the search page was full; narrow the query or use polymarket_get_events with a tag.
|
|
50481
51094
|
|
|
50482
51095
|
Examples:
|
|
50483
51096
|
- { "query": "fed decision", "limit": 5 } \u2014 Fed-rate brackets with current odds.
|
|
@@ -50514,16 +51127,20 @@ Don't use when:
|
|
|
50514
51127
|
const limit = parseLimit(args?.limit);
|
|
50515
51128
|
const active = parseOptionalBoolean(args?.active, "active", true);
|
|
50516
51129
|
const closed = parseOptionalBoolean(args?.closed, "closed", false);
|
|
50517
|
-
const scanLimit = query ?
|
|
50518
|
-
const
|
|
50519
|
-
|
|
51130
|
+
const scanLimit = query ? SEARCH_EVENT_LIMIT : limit;
|
|
51131
|
+
const search = query ? await searchGammaMarkets(query, closed) : null;
|
|
51132
|
+
const listing = search ?? await fetchGammaMarkets({
|
|
51133
|
+
limit,
|
|
50520
51134
|
active,
|
|
50521
51135
|
closed,
|
|
50522
51136
|
order: "volume24hr",
|
|
50523
51137
|
ascending: false
|
|
50524
51138
|
});
|
|
50525
|
-
const
|
|
50526
|
-
const
|
|
51139
|
+
const { url, rows } = listing;
|
|
51140
|
+
const mapped = rows.map(toMarketRow2).filter(
|
|
51141
|
+
(row) => !query || row.active === active && row.closed === closed
|
|
51142
|
+
);
|
|
51143
|
+
const exact = query ? mapped.filter(
|
|
50527
51144
|
(row) => matchesQuery(
|
|
50528
51145
|
[
|
|
50529
51146
|
row.question,
|
|
@@ -50534,19 +51151,22 @@ Don't use when:
|
|
|
50534
51151
|
],
|
|
50535
51152
|
query
|
|
50536
51153
|
)
|
|
50537
|
-
) : mapped;
|
|
51154
|
+
).sort((a, b) => (b.volume24hr ?? 0) - (a.volume24hr ?? 0)) : mapped;
|
|
51155
|
+
const fuzzy = Boolean(query) && exact.length === 0;
|
|
51156
|
+
const matched = fuzzy ? mapped : exact;
|
|
51157
|
+
const scanned = search ? search.eventCount : rows.length;
|
|
50538
51158
|
const items = matched.slice(0, limit);
|
|
50539
51159
|
return okResult(url, {
|
|
50540
51160
|
filters: { query, active, closed, limit },
|
|
50541
51161
|
scan: {
|
|
50542
|
-
scanned
|
|
51162
|
+
scanned,
|
|
50543
51163
|
scan_limit: scanLimit,
|
|
50544
51164
|
matched: matched.length,
|
|
50545
|
-
order: "volume24hr desc"
|
|
51165
|
+
order: fuzzy ? "relevance (no market contains every term)" : "volume24hr desc"
|
|
50546
51166
|
},
|
|
50547
51167
|
rows: items,
|
|
50548
51168
|
returnedCount: items.length,
|
|
50549
|
-
may_have_more: matched.length > limit ||
|
|
51169
|
+
may_have_more: matched.length > limit || scanned >= scanLimit
|
|
50550
51170
|
});
|
|
50551
51171
|
} catch (error) {
|
|
50552
51172
|
return errorResult3(toolName, error);
|
|
@@ -50568,14 +51188,16 @@ Args:
|
|
|
50568
51188
|
- slug (string, optional): one event's URL slug (exact); other filters are ignored.
|
|
50569
51189
|
- active (boolean, optional): Polymarket active flag filter (default true).
|
|
50570
51190
|
- closed (boolean, optional): Polymarket closed flag filter (default false).
|
|
51191
|
+
- order (string, optional): "volume24hr" (default, trending: most traded in the last 24h), "volume" (biggest by lifetime volume), "liquidity" (deepest books), or "endDate" (closing soonest, from now on). Biggest first for the volume and liquidity sorts.
|
|
50571
51192
|
${PAGING_ARGS_DOC}
|
|
50572
51193
|
|
|
50573
51194
|
Returns:
|
|
50574
51195
|
${ENVELOPE_DOC}
|
|
50575
|
-
"filters", "markets_preview_limit" (nested previews actually kept per event, ${EVENT_MARKETS_PREVIEW} unless the size budget forced fewer), "rows_dropped_for_size" (0 unless headline data alone overran the budget), and "rows": [{ "id", "slug", "title", "active", "closed", "endDate", "liquidity", "volume", "volume24hr", "negRisk", "tags", "marketCount", "markets": [{ "slug", "question", "groupItemTitle", "conditionId", "outcomes", "outcomePrices", "clobTokenIds", "volume24hr" }], "marketsTruncated" }]. Rows
|
|
51196
|
+
"filters" (including the order applied), "markets_preview_limit" (nested previews actually kept per event, ${EVENT_MARKETS_PREVIEW} unless the size budget forced fewer), "rows_dropped_for_size" (0 unless headline data alone overran the budget), and "rows": [{ "id", "slug", "title", "active", "closed", "endDate", "liquidity", "volume", "volume24hr", "negRisk", "tags", "marketCount", "markets": [{ "slug", "question", "groupItemTitle", "conditionId", "outcomes", "outcomePrices", "clobTokenIds", "volume24hr" }], "marketsTruncated" }]. Rows follow the requested order (24h volume, desc, by default). returnedCount is always the number of rows actually in the response. Large limits are slow because Gamma nests every market of every event.
|
|
50576
51197
|
|
|
50577
51198
|
Examples:
|
|
50578
51199
|
- { "tag": "crypto", "limit": 5 } \u2014 the busiest crypto events.
|
|
51200
|
+
- { "order": "volume", "limit": 5 } \u2014 the biggest events by all-time volume (not just today's trend).
|
|
50579
51201
|
- { "slug": "presidential-election-winner-2028" } \u2014 one event with its top-${EVENT_MARKETS_PREVIEW} candidates by odds.
|
|
50580
51202
|
- { "limit": 10, "offset": 10 } \u2014 the next page of the most active events.
|
|
50581
51203
|
|
|
@@ -50600,6 +51222,11 @@ Don't use when:
|
|
|
50600
51222
|
},
|
|
50601
51223
|
active: ACTIVE_SCHEMA,
|
|
50602
51224
|
closed: CLOSED_SCHEMA,
|
|
51225
|
+
order: {
|
|
51226
|
+
type: "string",
|
|
51227
|
+
enum: [...EVENT_ORDERS],
|
|
51228
|
+
description: 'Sort: "volume24hr" (default, trending), "volume" (biggest all-time), "liquidity", or "endDate" (closing soonest from now).'
|
|
51229
|
+
},
|
|
50603
51230
|
limit: LIMIT_SCHEMA,
|
|
50604
51231
|
offset: OFFSET_SCHEMA
|
|
50605
51232
|
}
|
|
@@ -50615,19 +51242,19 @@ Don't use when:
|
|
|
50615
51242
|
const offset = parseOffset(args?.offset);
|
|
50616
51243
|
const active = parseOptionalBoolean(args?.active, "active", true);
|
|
50617
51244
|
const closed = parseOptionalBoolean(args?.closed, "closed", false);
|
|
51245
|
+
const sort = eventOrderParams(args?.order);
|
|
50618
51246
|
const { url, rows } = slug ? await fetchGammaEvents({ slug }) : await fetchGammaEvents({
|
|
50619
51247
|
limit,
|
|
50620
51248
|
offset,
|
|
50621
51249
|
active,
|
|
50622
51250
|
closed,
|
|
50623
51251
|
tag_slug: tag ?? void 0,
|
|
50624
|
-
|
|
50625
|
-
ascending: false
|
|
51252
|
+
...sort
|
|
50626
51253
|
});
|
|
50627
51254
|
const bounded = boundEventRows(rows);
|
|
50628
51255
|
const items = bounded.rows;
|
|
50629
51256
|
return okResult(url, {
|
|
50630
|
-
filters: slug ? { slug } : { tag, active, closed, limit, offset },
|
|
51257
|
+
filters: slug ? { slug } : { tag, active, closed, order: sort.order, limit, offset },
|
|
50631
51258
|
markets_preview_limit: bounded.marketsPreviewLimit,
|
|
50632
51259
|
rows_dropped_for_size: bounded.rowsDropped,
|
|
50633
51260
|
rows: items,
|
|
@@ -51039,7 +51666,7 @@ function isAbortError(error) {
|
|
|
51039
51666
|
const name = error?.name;
|
|
51040
51667
|
return name === "AbortError" || name === "TimeoutError";
|
|
51041
51668
|
}
|
|
51042
|
-
async function fetchJsonWithTimeout(url, label, timeoutMs = SIGNALS_FETCH_TIMEOUT_MS) {
|
|
51669
|
+
async function fetchJsonWithTimeout(url, label, timeoutMs = SIGNALS_FETCH_TIMEOUT_MS, headers = {}) {
|
|
51043
51670
|
const controller = new AbortController();
|
|
51044
51671
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
51045
51672
|
try {
|
|
@@ -51049,7 +51676,8 @@ async function fetchJsonWithTimeout(url, label, timeoutMs = SIGNALS_FETCH_TIMEOU
|
|
|
51049
51676
|
signal: controller.signal,
|
|
51050
51677
|
headers: {
|
|
51051
51678
|
Accept: "application/json",
|
|
51052
|
-
"User-Agent": "hive-mcp-server/1.0.0"
|
|
51679
|
+
"User-Agent": "hive-mcp-server/1.0.0",
|
|
51680
|
+
...headers
|
|
51053
51681
|
}
|
|
51054
51682
|
});
|
|
51055
51683
|
} catch (error) {
|
|
@@ -51910,10 +52538,10 @@ async function fetchHackRows() {
|
|
|
51910
52538
|
}
|
|
51911
52539
|
|
|
51912
52540
|
// src/tools/signals/tools.ts
|
|
51913
|
-
function errorResult4(
|
|
52541
|
+
function errorResult4(tool100, error, extra = {}) {
|
|
51914
52542
|
if (error instanceof SignalsInputError) {
|
|
51915
52543
|
return asTextContentResult(
|
|
51916
|
-
{ error: error.message, runtime_status: "invalid_input", tool:
|
|
52544
|
+
{ error: error.message, runtime_status: "invalid_input", tool: tool100, ...extra },
|
|
51917
52545
|
true
|
|
51918
52546
|
);
|
|
51919
52547
|
}
|
|
@@ -51922,7 +52550,7 @@ function errorResult4(tool96, error, extra = {}) {
|
|
|
51922
52550
|
{
|
|
51923
52551
|
error: message,
|
|
51924
52552
|
runtime_status: classifyRuntimeStatusFromError(message),
|
|
51925
|
-
tool:
|
|
52553
|
+
tool: tool100,
|
|
51926
52554
|
...extra
|
|
51927
52555
|
},
|
|
51928
52556
|
true
|
|
@@ -52294,6 +52922,334 @@ Don't use when:
|
|
|
52294
52922
|
];
|
|
52295
52923
|
}
|
|
52296
52924
|
|
|
52925
|
+
// src/tools/blockscout/tools.ts
|
|
52926
|
+
import Coingecko2 from "@coingecko/coingecko-typescript";
|
|
52927
|
+
var BLOCKSCOUT_PROVIDER = "Blockscout";
|
|
52928
|
+
var HOLDERS_PAGE_SIZE = 50;
|
|
52929
|
+
var DEFAULT_MAX_PAGES = 20;
|
|
52930
|
+
var MAX_PAGES = 40;
|
|
52931
|
+
var TIME_BUDGET_MS = 2e4;
|
|
52932
|
+
var CHAINS = {
|
|
52933
|
+
eth: { chainId: 1, host: "eth.blockscout.com", gecko: "eth" },
|
|
52934
|
+
base: { chainId: 8453, host: "base.blockscout.com", gecko: "base" },
|
|
52935
|
+
optimism: { chainId: 10, host: "explorer.optimism.io", gecko: "optimism" },
|
|
52936
|
+
arbitrum: {
|
|
52937
|
+
chainId: 42161,
|
|
52938
|
+
host: "arbitrum.blockscout.com",
|
|
52939
|
+
gecko: "arbitrum"
|
|
52940
|
+
},
|
|
52941
|
+
polygon: {
|
|
52942
|
+
chainId: 137,
|
|
52943
|
+
host: "polygon.blockscout.com",
|
|
52944
|
+
gecko: "polygon_pos"
|
|
52945
|
+
}
|
|
52946
|
+
};
|
|
52947
|
+
var CHAIN_ALIASES = {
|
|
52948
|
+
ethereum: "eth",
|
|
52949
|
+
mainnet: "eth",
|
|
52950
|
+
"1": "eth",
|
|
52951
|
+
"8453": "base",
|
|
52952
|
+
op: "optimism",
|
|
52953
|
+
"10": "optimism",
|
|
52954
|
+
arb: "arbitrum",
|
|
52955
|
+
"42161": "arbitrum",
|
|
52956
|
+
matic: "polygon",
|
|
52957
|
+
polygon_pos: "polygon",
|
|
52958
|
+
"137": "polygon"
|
|
52959
|
+
};
|
|
52960
|
+
var USD_TIERS = [
|
|
52961
|
+
{ label: ">$10k", min_usd: 1e4, max_usd: null },
|
|
52962
|
+
{ label: "$1k-$10k", min_usd: 1e3, max_usd: 1e4 },
|
|
52963
|
+
{ label: "$100-$1k", min_usd: 100, max_usd: 1e3 },
|
|
52964
|
+
{ label: "$1-$100", min_usd: 1, max_usd: 100 },
|
|
52965
|
+
{ label: "<$1", min_usd: 0, max_usd: 1 }
|
|
52966
|
+
];
|
|
52967
|
+
function tallyTiers(rows, totalHolders, stop) {
|
|
52968
|
+
const reachedBottom = stop === "end_of_holders";
|
|
52969
|
+
const smallest = rows.length > 0 ? rows[rows.length - 1].usd : null;
|
|
52970
|
+
const tiers = USD_TIERS.map((tier) => {
|
|
52971
|
+
const members = rows.filter(
|
|
52972
|
+
(row) => row.usd >= tier.min_usd && (tier.max_usd === null || row.usd < tier.max_usd)
|
|
52973
|
+
);
|
|
52974
|
+
const exact = reachedBottom || smallest !== null && smallest < tier.min_usd;
|
|
52975
|
+
const reached = smallest !== null && smallest < (tier.max_usd ?? Infinity);
|
|
52976
|
+
return {
|
|
52977
|
+
...tier,
|
|
52978
|
+
count: exact || reached ? members.length : null,
|
|
52979
|
+
contracts: exact || reached ? members.filter((r) => r.is_contract).length : null,
|
|
52980
|
+
exact,
|
|
52981
|
+
lower_bound: !exact && reached
|
|
52982
|
+
};
|
|
52983
|
+
});
|
|
52984
|
+
const dust = tiers[tiers.length - 1];
|
|
52985
|
+
if (!dust.exact && stop === "below_smallest_tier" && totalHolders !== null) {
|
|
52986
|
+
const above = rows.filter((row) => row.usd >= 1).length;
|
|
52987
|
+
dust.count = Math.max(totalHolders - above, 0);
|
|
52988
|
+
dust.contracts = null;
|
|
52989
|
+
dust.exact = true;
|
|
52990
|
+
dust.lower_bound = false;
|
|
52991
|
+
}
|
|
52992
|
+
const real = tiers.filter((tier) => tier.min_usd >= 100);
|
|
52993
|
+
return {
|
|
52994
|
+
tiers,
|
|
52995
|
+
holders_100_usd_plus: {
|
|
52996
|
+
count: real.reduce((sum, tier) => sum + (tier.count ?? 0), 0),
|
|
52997
|
+
exact: real.every((tier) => tier.exact)
|
|
52998
|
+
},
|
|
52999
|
+
exhaustive: tiers.every((tier) => tier.exact),
|
|
53000
|
+
smallest_scanned_usd: smallest
|
|
53001
|
+
};
|
|
53002
|
+
}
|
|
53003
|
+
function parseArgs(args) {
|
|
53004
|
+
const rawNetwork = String(args?.network ?? args?.chain ?? "").trim().toLowerCase();
|
|
53005
|
+
const key = CHAIN_ALIASES[rawNetwork] ?? rawNetwork;
|
|
53006
|
+
const chain = CHAINS[key];
|
|
53007
|
+
if (!chain) {
|
|
53008
|
+
throw new SignalsInputError(
|
|
53009
|
+
`network must be one of ${Object.keys(CHAINS).join(", ")} (or a chain id); got ${JSON.stringify(args?.network ?? null)}.`
|
|
53010
|
+
);
|
|
53011
|
+
}
|
|
53012
|
+
const address = String(args?.address ?? "").trim();
|
|
53013
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {
|
|
53014
|
+
throw new SignalsInputError(
|
|
53015
|
+
`address must be a 0x token contract address; got ${JSON.stringify(args?.address ?? null)}.`
|
|
53016
|
+
);
|
|
53017
|
+
}
|
|
53018
|
+
const priceOverride = args?.price_usd === void 0 || args?.price_usd === null ? null : parseNumberArg(args.price_usd, "price_usd", Number.MIN_VALUE, 1e12, 0);
|
|
53019
|
+
return {
|
|
53020
|
+
network: key,
|
|
53021
|
+
chain,
|
|
53022
|
+
address,
|
|
53023
|
+
maxPages: parseIntegerArg(
|
|
53024
|
+
args?.max_pages,
|
|
53025
|
+
"max_pages",
|
|
53026
|
+
1,
|
|
53027
|
+
MAX_PAGES,
|
|
53028
|
+
DEFAULT_MAX_PAGES
|
|
53029
|
+
),
|
|
53030
|
+
priceOverride
|
|
53031
|
+
};
|
|
53032
|
+
}
|
|
53033
|
+
function blockscoutGet(chain, path) {
|
|
53034
|
+
const apiKey = getEnvVar("BLOCKSCOUT_API_KEY");
|
|
53035
|
+
const url = apiKey ? `https://api.blockscout.com/${chain.chainId}/api/v2${path}` : `https://${chain.host}/api/v2${path}`;
|
|
53036
|
+
return fetchJsonWithTimeout(
|
|
53037
|
+
url,
|
|
53038
|
+
`Blockscout (${chain.chainId})`,
|
|
53039
|
+
void 0,
|
|
53040
|
+
apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
|
|
53041
|
+
).catch((error) => {
|
|
53042
|
+
if (error instanceof SignalsHttpError && error.status === 404) {
|
|
53043
|
+
throw new SignalsInputError(
|
|
53044
|
+
`Blockscout has no token at this address on chain ${chain.chainId}.`
|
|
53045
|
+
);
|
|
53046
|
+
}
|
|
53047
|
+
if (!apiKey && error instanceof SignalsHttpError && (error.status === 403 || error.status === 402)) {
|
|
53048
|
+
throw new Error(
|
|
53049
|
+
`Blockscout's public explorer for chain ${chain.chainId} refused the request (HTTP ${error.status}, bot protection). Set BLOCKSCOUT_API_KEY (free API key at dev.blockscout.com) to use the Blockscout PRO API.`
|
|
53050
|
+
);
|
|
53051
|
+
}
|
|
53052
|
+
throw error;
|
|
53053
|
+
});
|
|
53054
|
+
}
|
|
53055
|
+
var ownCoinGecko = null;
|
|
53056
|
+
async function resolvePrice(chain, address, token) {
|
|
53057
|
+
const blockscoutRate = parseFiniteNumber3(token.exchange_rate);
|
|
53058
|
+
if (blockscoutRate !== null && blockscoutRate > 0) {
|
|
53059
|
+
return { price: blockscoutRate, source: "blockscout_exchange_rate" };
|
|
53060
|
+
}
|
|
53061
|
+
try {
|
|
53062
|
+
if (!ownCoinGecko)
|
|
53063
|
+
ownCoinGecko = new Coingecko2(getCoinGeckoClientOptions());
|
|
53064
|
+
const result2 = await ownCoinGecko.onchain.simple.networks.tokenPrice.getAddresses(
|
|
53065
|
+
address,
|
|
53066
|
+
{ network: chain.gecko }
|
|
53067
|
+
);
|
|
53068
|
+
const prices = result2?.data?.attributes?.token_prices ?? {};
|
|
53069
|
+
const price = parseFiniteNumber3(
|
|
53070
|
+
prices[address.toLowerCase()] ?? prices[address]
|
|
53071
|
+
);
|
|
53072
|
+
if (price !== null && price > 0) {
|
|
53073
|
+
return { price, source: "coingecko_onchain_price" };
|
|
53074
|
+
}
|
|
53075
|
+
} catch {
|
|
53076
|
+
}
|
|
53077
|
+
return { price: null, source: null };
|
|
53078
|
+
}
|
|
53079
|
+
async function getTokenHolderDistribution(args, now = Date.now) {
|
|
53080
|
+
const { network, chain, address, maxPages, priceOverride } = parseArgs(args);
|
|
53081
|
+
const started = now();
|
|
53082
|
+
const token = await blockscoutGet(chain, `/tokens/${address}`) ?? {};
|
|
53083
|
+
if (token.type !== void 0 && token.type !== "ERC-20") {
|
|
53084
|
+
throw new SignalsInputError(
|
|
53085
|
+
`this address is a ${String(token.type)} token; holder tiers are computed for ERC-20 tokens only.`
|
|
53086
|
+
);
|
|
53087
|
+
}
|
|
53088
|
+
const decimals = parseFiniteNumber3(token.decimals);
|
|
53089
|
+
if (decimals === null) {
|
|
53090
|
+
throw new Error("Blockscout returned no decimals for this token");
|
|
53091
|
+
}
|
|
53092
|
+
const totalHolders = parseFiniteNumber3(token.holders_count ?? token.holders);
|
|
53093
|
+
const { price, source: priceSource } = priceOverride !== null ? { price: priceOverride, source: "caller" } : await resolvePrice(chain, address, token);
|
|
53094
|
+
const base = {
|
|
53095
|
+
provider: BLOCKSCOUT_PROVIDER,
|
|
53096
|
+
network,
|
|
53097
|
+
chain_id: chain.chainId,
|
|
53098
|
+
address,
|
|
53099
|
+
token: {
|
|
53100
|
+
name: token.name ?? null,
|
|
53101
|
+
symbol: token.symbol ?? null,
|
|
53102
|
+
decimals,
|
|
53103
|
+
total_supply: token.total_supply ?? null
|
|
53104
|
+
},
|
|
53105
|
+
total_holders: totalHolders,
|
|
53106
|
+
price_usd: price,
|
|
53107
|
+
price_source: priceSource
|
|
53108
|
+
};
|
|
53109
|
+
if (price === null) {
|
|
53110
|
+
return {
|
|
53111
|
+
...base,
|
|
53112
|
+
runtime_status: "degraded",
|
|
53113
|
+
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
53114
|
+
tiers: null,
|
|
53115
|
+
note: "No USD price was found for this token, so holders could not be tiered by value. total_holders counts every address with a positive balance, dust included. Pass price_usd to tier anyway."
|
|
53116
|
+
};
|
|
53117
|
+
}
|
|
53118
|
+
const rows = [];
|
|
53119
|
+
let pages = 0;
|
|
53120
|
+
let cursor = "";
|
|
53121
|
+
let stop = "end_of_holders";
|
|
53122
|
+
for (; ; ) {
|
|
53123
|
+
if (pages >= maxPages) {
|
|
53124
|
+
stop = "max_pages";
|
|
53125
|
+
break;
|
|
53126
|
+
}
|
|
53127
|
+
if (now() - started > TIME_BUDGET_MS) {
|
|
53128
|
+
stop = "time_budget";
|
|
53129
|
+
break;
|
|
53130
|
+
}
|
|
53131
|
+
const page = await blockscoutGet(
|
|
53132
|
+
chain,
|
|
53133
|
+
`/tokens/${address}/holders${cursor}`
|
|
53134
|
+
) ?? {};
|
|
53135
|
+
pages += 1;
|
|
53136
|
+
const items = Array.isArray(page.items) ? page.items : [];
|
|
53137
|
+
let belowFloor = false;
|
|
53138
|
+
for (const item of items) {
|
|
53139
|
+
const holder = item ?? {};
|
|
53140
|
+
const units = parseFiniteNumber3(holder.value);
|
|
53141
|
+
if (units === null) continue;
|
|
53142
|
+
const usd2 = units / 10 ** decimals * price;
|
|
53143
|
+
rows.push({ usd: usd2, is_contract: holder.address?.is_contract === true });
|
|
53144
|
+
if (usd2 < 1) {
|
|
53145
|
+
belowFloor = true;
|
|
53146
|
+
break;
|
|
53147
|
+
}
|
|
53148
|
+
}
|
|
53149
|
+
if (belowFloor) {
|
|
53150
|
+
stop = "below_smallest_tier";
|
|
53151
|
+
break;
|
|
53152
|
+
}
|
|
53153
|
+
const next = page.next_page_params;
|
|
53154
|
+
if (!next || typeof next !== "object" || items.length === 0) break;
|
|
53155
|
+
cursor = `?${new URLSearchParams(
|
|
53156
|
+
Object.entries(next).map(([k, v]) => [
|
|
53157
|
+
k,
|
|
53158
|
+
String(v)
|
|
53159
|
+
])
|
|
53160
|
+
).toString()}`;
|
|
53161
|
+
}
|
|
53162
|
+
const tally = tallyTiers(rows, totalHolders, stop);
|
|
53163
|
+
return {
|
|
53164
|
+
...base,
|
|
53165
|
+
runtime_status: "ok",
|
|
53166
|
+
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
53167
|
+
holders_scanned: rows.length,
|
|
53168
|
+
pages_fetched: pages,
|
|
53169
|
+
stopped_reason: stop,
|
|
53170
|
+
...tally,
|
|
53171
|
+
lower_bound: !tally.exhaustive,
|
|
53172
|
+
note: (tally.exhaustive ? "Every tier is exact." : `The walk stopped (${stop}) at a holder worth $${tally.smallest_scanned_usd?.toFixed(2)}; tiers marked exact are complete, lower_bound tiers are at least the count shown, and null tiers were not reached. Raise max_pages (up to ${MAX_PAGES}) to go deeper.`) + " Counts include contracts (pools, vesting, treasuries); each tier's contracts field says how many. USD values use price_usd at fetch time."
|
|
53173
|
+
};
|
|
53174
|
+
}
|
|
53175
|
+
function getAllBlockscoutEndpoints() {
|
|
53176
|
+
return [
|
|
53177
|
+
{
|
|
53178
|
+
metadata: {
|
|
53179
|
+
resource: "blockscout.tokens.holders",
|
|
53180
|
+
operation: "read",
|
|
53181
|
+
tags: ["blockscout", "tokens", "holders", "distribution", "onchain"]
|
|
53182
|
+
},
|
|
53183
|
+
tool: {
|
|
53184
|
+
name: "get_token_holder_distribution",
|
|
53185
|
+
description: `Count a token's holders by USD balance: total holders plus how many hold >$10k, $1k-$10k, $100-$1k, $1-$100, and under $1 (dust), from Blockscout's holder list. Answers "how many real (non-dust) holders does this token have?". Keyless on EVM chains: ${Object.keys(CHAINS).join(", ")}.
|
|
53186
|
+
|
|
53187
|
+
Args:
|
|
53188
|
+
- network (string, required): ${Object.keys(CHAINS).join(", ")}, or a chain id (1, 8453, 10, 42161, 137).
|
|
53189
|
+
- address (string, required): ERC-20 token contract address.
|
|
53190
|
+
- max_pages (integer, optional): holder pages of ${HOLDERS_PAGE_SIZE} to walk, 1-${MAX_PAGES} (default ${DEFAULT_MAX_PAGES}). The walk also stops after ${TIME_BUDGET_MS / 1e3} s, at the end of the list, or at the first holder under $1.
|
|
53191
|
+
- price_usd (number, optional): USD price to tier with. Default: Blockscout's exchange rate, then the GeckoTerminal on-chain price.
|
|
53192
|
+
|
|
53193
|
+
Returns:
|
|
53194
|
+
{ "provider": "Blockscout", "runtime_status", "fetched_at", "network", "chain_id", "address", "token": { "name", "symbol", "decimals", "total_supply" }, "total_holders", "price_usd", "price_source", "holders_scanned", "pages_fetched", "stopped_reason", "tiers": [{ "label", "min_usd", "max_usd", "count", "contracts", "exact", "lower_bound" }], "holders_100_usd_plus": { "count", "exact" }, "exhaustive", "lower_bound", "smallest_scanned_usd", "note" }. A tier with exact=false and lower_bound=true holds at least count holders; count null means the walk never reached it.
|
|
53195
|
+
|
|
53196
|
+
Examples:
|
|
53197
|
+
- { "network": "base", "address": "0x91dA780BC7f4B7Cf19ABE90411a2a296Ec5FF787" }
|
|
53198
|
+
- { "network": "eth", "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "max_pages": 5 }
|
|
53199
|
+
|
|
53200
|
+
Use when:
|
|
53201
|
+
- You need holder counts above a USD size, or how much of the holder count is dust.
|
|
53202
|
+
Don't use when:
|
|
53203
|
+
- You need the largest holders and their share of supply (use get_token_top_holders).`,
|
|
53204
|
+
inputSchema: {
|
|
53205
|
+
type: "object",
|
|
53206
|
+
properties: {
|
|
53207
|
+
network: {
|
|
53208
|
+
type: "string",
|
|
53209
|
+
description: `Chain: ${Object.keys(CHAINS).join(", ")}, or a chain id.`
|
|
53210
|
+
},
|
|
53211
|
+
address: {
|
|
53212
|
+
type: "string",
|
|
53213
|
+
description: "ERC-20 token contract address (0x...)."
|
|
53214
|
+
},
|
|
53215
|
+
max_pages: {
|
|
53216
|
+
type: "integer",
|
|
53217
|
+
minimum: 1,
|
|
53218
|
+
maximum: MAX_PAGES,
|
|
53219
|
+
default: DEFAULT_MAX_PAGES,
|
|
53220
|
+
description: `Holder pages of ${HOLDERS_PAGE_SIZE} to walk (1-${MAX_PAGES}).`
|
|
53221
|
+
},
|
|
53222
|
+
price_usd: {
|
|
53223
|
+
type: "number",
|
|
53224
|
+
exclusiveMinimum: 0,
|
|
53225
|
+
description: "USD price to tier with; default Blockscout's rate, then GeckoTerminal."
|
|
53226
|
+
}
|
|
53227
|
+
},
|
|
53228
|
+
required: ["network", "address"],
|
|
53229
|
+
additionalProperties: false
|
|
53230
|
+
},
|
|
53231
|
+
annotations: VOLATILE_READ_ANNOTATIONS
|
|
53232
|
+
},
|
|
53233
|
+
handler: async (_client, args) => {
|
|
53234
|
+
try {
|
|
53235
|
+
return asTextContentResult(await getTokenHolderDistribution(args));
|
|
53236
|
+
} catch (error) {
|
|
53237
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
53238
|
+
return asTextContentResult(
|
|
53239
|
+
{
|
|
53240
|
+
error: message,
|
|
53241
|
+
runtime_status: error instanceof SignalsInputError ? "invalid_input" : classifyRuntimeStatusFromError(message),
|
|
53242
|
+
tool: "get_token_holder_distribution",
|
|
53243
|
+
provider: BLOCKSCOUT_PROVIDER
|
|
53244
|
+
},
|
|
53245
|
+
true
|
|
53246
|
+
);
|
|
53247
|
+
}
|
|
53248
|
+
}
|
|
53249
|
+
}
|
|
53250
|
+
];
|
|
53251
|
+
}
|
|
53252
|
+
|
|
52297
53253
|
// src/freeWriteEndpoints.ts
|
|
52298
53254
|
var FREE_WRITE_ENDPOINTS = /* @__PURE__ */ new Set([
|
|
52299
53255
|
// Feedback is a bounded, rate-limited insert into Hive's own table. It
|
|
@@ -52863,7 +53819,7 @@ Don't use when:
|
|
|
52863
53819
|
}
|
|
52864
53820
|
|
|
52865
53821
|
// src/wrapper/index.ts
|
|
52866
|
-
import
|
|
53822
|
+
import Coingecko3 from "@coingecko/coingecko-typescript";
|
|
52867
53823
|
|
|
52868
53824
|
// src/utils/descriptionGenerator.ts
|
|
52869
53825
|
function needsEnrichment(description) {
|
|
@@ -53538,7 +54494,7 @@ var SCHEMA_PATTERNS = [
|
|
|
53538
54494
|
TOKEN_DILIGENCE_OUTPUT_SCHEMA
|
|
53539
54495
|
],
|
|
53540
54496
|
[
|
|
53541
|
-
/^(alchemy_get_tokens_by_wallet|alchemy_get_token_balances_by_wallet|alchemy_get_native_balance|alchemy_get_asset_transfers|moralis_get_wallet_history|moralis_get_wallet_profitability|moralis_get_wallet_profitability_summary|moralis_get_wallet_pnl|moralis_get_wallet_pnl_summary|moralis_get_wallet_defi_positions|moralis_get_wallet_defi_summary|moralis_get_wallet_insight|helius_get_wallet_balances|helius_get_wallet_history|helius_get_wallet_transfers|get_wallet_balances|get_wallet_stats|get_wallet_chart)$/,
|
|
54497
|
+
/^(get_wallet_balances|get_wallet_pnl|get_wallet_trades|get_wallet_transfers|alchemy_get_tokens_by_wallet|alchemy_get_token_balances_by_wallet|alchemy_get_native_balance|alchemy_get_asset_transfers|moralis_get_wallet_history|moralis_get_wallet_profitability|moralis_get_wallet_profitability_summary|moralis_get_wallet_pnl|moralis_get_wallet_pnl_summary|moralis_get_wallet_defi_positions|moralis_get_wallet_defi_summary|moralis_get_wallet_insight|helius_get_wallet_balances|helius_get_wallet_history|helius_get_wallet_transfers|get_wallet_balances|get_wallet_stats|get_wallet_chart)$/,
|
|
53542
54498
|
WALLET_INVESTIGATION_OUTPUT_SCHEMA
|
|
53543
54499
|
],
|
|
53544
54500
|
[
|
|
@@ -53634,6 +54590,11 @@ var CHECK_TOKEN_SAFETY_OUTPUT_SCHEMA = {
|
|
|
53634
54590
|
additionalProperties: true,
|
|
53635
54591
|
description: "Raw provider risk datapoints; '1' means flagged. A missing key means the provider did not report that signal, not that it passed."
|
|
53636
54592
|
},
|
|
54593
|
+
summary: {
|
|
54594
|
+
type: "object",
|
|
54595
|
+
additionalProperties: true,
|
|
54596
|
+
description: "Concise scam signals: verdicts (plain-language list), lp (lock state and top LP holder), top_holders (largest holder and top-10 share, DEX pairs excluded), creator, owner. Percents are 0-100."
|
|
54597
|
+
},
|
|
53637
54598
|
holder_count: {},
|
|
53638
54599
|
token_name: { type: "string" },
|
|
53639
54600
|
note: { type: "string" }
|
|
@@ -54055,11 +55016,11 @@ var TENDERLY_REST_TOOLS = /* @__PURE__ */ new Set([
|
|
|
54055
55016
|
"tenderly_estimate_gas",
|
|
54056
55017
|
"tenderly_decode_calldata"
|
|
54057
55018
|
]);
|
|
54058
|
-
function matchesProvider(provider, toolName,
|
|
55019
|
+
function matchesProvider(provider, toolName, metadata104) {
|
|
54059
55020
|
const normalizedProvider = provider.toLowerCase();
|
|
54060
55021
|
const normalizedToolName = toolName.toLowerCase();
|
|
54061
|
-
const resource =
|
|
54062
|
-
const tags =
|
|
55022
|
+
const resource = metadata104.resource.toLowerCase();
|
|
55023
|
+
const tags = metadata104.tags.map((tag) => tag.toLowerCase());
|
|
54063
55024
|
return normalizedToolName.startsWith(`${normalizedProvider}_`) || normalizedToolName.includes(`_${normalizedProvider}_`) || resource.startsWith(normalizedProvider) || resource.includes(`${normalizedProvider}.`) || tags.includes(normalizedProvider);
|
|
54064
55025
|
}
|
|
54065
55026
|
var PROVIDER_CATALOG = [
|
|
@@ -54070,7 +55031,7 @@ var PROVIDER_CATALOG = [
|
|
|
54070
55031
|
type: "MCP discovery and compatibility tools",
|
|
54071
55032
|
clientStrategy: "hive",
|
|
54072
55033
|
alwaysAvailable: true,
|
|
54073
|
-
matches: (toolName,
|
|
55034
|
+
matches: (toolName, metadata104) => toolName.startsWith("hive_") || metadata104.resource === "dynamic_tools" || metadata104.tags.includes("hive")
|
|
54074
55035
|
},
|
|
54075
55036
|
{
|
|
54076
55037
|
id: "coingecko",
|
|
@@ -54079,7 +55040,7 @@ var PROVIDER_CATALOG = [
|
|
|
54079
55040
|
type: "Market data, prices, and metadata",
|
|
54080
55041
|
clientStrategy: "shared-coingecko",
|
|
54081
55042
|
alwaysAvailable: true,
|
|
54082
|
-
matches: (toolName,
|
|
55043
|
+
matches: (toolName, metadata104) => matchesProvider("coingecko", toolName, metadata104)
|
|
54083
55044
|
},
|
|
54084
55045
|
{
|
|
54085
55046
|
id: "goplus",
|
|
@@ -54091,7 +55052,7 @@ var PROVIDER_CATALOG = [
|
|
|
54091
55052
|
label: "GOPLUS_APP_KEY or GOPLUS_API_KEY, plus GOPLUS_APP_SECRET",
|
|
54092
55053
|
isConfigured: () => hasAnyEnvVar("GOPLUS_APP_KEY", "GOPLUS_API_KEY") && hasEnvVar("GOPLUS_APP_SECRET")
|
|
54093
55054
|
},
|
|
54094
|
-
matches: (toolName,
|
|
55055
|
+
matches: (toolName, metadata104) => matchesProvider("goplus", toolName, metadata104)
|
|
54095
55056
|
},
|
|
54096
55057
|
{
|
|
54097
55058
|
id: "ccxt",
|
|
@@ -54100,7 +55061,7 @@ var PROVIDER_CATALOG = [
|
|
|
54100
55061
|
type: "Centralized exchange market data and trading metadata",
|
|
54101
55062
|
clientStrategy: "shared-ccxt",
|
|
54102
55063
|
alwaysAvailable: true,
|
|
54103
|
-
matches: (toolName,
|
|
55064
|
+
matches: (toolName, metadata104) => matchesProvider("ccxt", toolName, metadata104)
|
|
54104
55065
|
},
|
|
54105
55066
|
{
|
|
54106
55067
|
id: "defillama",
|
|
@@ -54109,7 +55070,7 @@ var PROVIDER_CATALOG = [
|
|
|
54109
55070
|
type: "TVL, protocol analytics, yields, and bridge data",
|
|
54110
55071
|
clientStrategy: "internal",
|
|
54111
55072
|
alwaysAvailable: true,
|
|
54112
|
-
matches: (toolName,
|
|
55073
|
+
matches: (toolName, metadata104) => matchesProvider("defillama", toolName, metadata104)
|
|
54113
55074
|
},
|
|
54114
55075
|
{
|
|
54115
55076
|
// Listed BEFORE hyperliquid: rwa_get_hyperliquid_dex_perps contains the
|
|
@@ -54121,7 +55082,7 @@ var PROVIDER_CATALOG = [
|
|
|
54121
55082
|
type: "Tokenized real-world-asset perps: cross-venue funding, markets, and open interest across Hyperliquid HIP-3 builder dexes, Ostium, Avantis, Lighter, and Extended (keyless official APIs)",
|
|
54122
55083
|
clientStrategy: "internal",
|
|
54123
55084
|
alwaysAvailable: true,
|
|
54124
|
-
matches: (toolName,
|
|
55085
|
+
matches: (toolName, metadata104) => matchesProvider("rwa", toolName, metadata104)
|
|
54125
55086
|
},
|
|
54126
55087
|
{
|
|
54127
55088
|
id: "hyperliquid",
|
|
@@ -54130,7 +55091,7 @@ var PROVIDER_CATALOG = [
|
|
|
54130
55091
|
type: "Hyperliquid perpetual DEX: per-pair volume, funding, open interest, and builder DEXs (tokenized-stock perps)",
|
|
54131
55092
|
clientStrategy: "internal",
|
|
54132
55093
|
alwaysAvailable: true,
|
|
54133
|
-
matches: (toolName,
|
|
55094
|
+
matches: (toolName, metadata104) => matchesProvider("hyperliquid", toolName, metadata104)
|
|
54134
55095
|
},
|
|
54135
55096
|
{
|
|
54136
55097
|
// The Hive-native hive_archive_monitor tool contains the "_archive_"
|
|
@@ -54148,7 +55109,7 @@ var PROVIDER_CATALOG = [
|
|
|
54148
55109
|
"SHARPE_ARCHIVE_SUPABASE_SERVICE_ROLE_KEY"
|
|
54149
55110
|
)
|
|
54150
55111
|
},
|
|
54151
|
-
matches: (toolName,
|
|
55112
|
+
matches: (toolName, metadata104) => matchesProvider("archive", toolName, metadata104)
|
|
54152
55113
|
},
|
|
54153
55114
|
{
|
|
54154
55115
|
id: "polymarket",
|
|
@@ -54157,7 +55118,7 @@ var PROVIDER_CATALOG = [
|
|
|
54157
55118
|
type: "Polymarket prediction markets: market and event search, market-implied odds, CLOB order books and price history, per-wallet positions and activity (keyless public Gamma, CLOB and Data APIs)",
|
|
54158
55119
|
clientStrategy: "internal",
|
|
54159
55120
|
alwaysAvailable: true,
|
|
54160
|
-
matches: (toolName,
|
|
55121
|
+
matches: (toolName, metadata104) => matchesProvider("polymarket", toolName, metadata104)
|
|
54161
55122
|
},
|
|
54162
55123
|
{
|
|
54163
55124
|
id: "alternative",
|
|
@@ -54166,7 +55127,7 @@ var PROVIDER_CATALOG = [
|
|
|
54166
55127
|
type: "Crypto Fear & Greed Index: daily market-wide sentiment composite since 2018 (keyless)",
|
|
54167
55128
|
clientStrategy: "internal",
|
|
54168
55129
|
alwaysAvailable: true,
|
|
54169
|
-
matches: (_toolName,
|
|
55130
|
+
matches: (_toolName, metadata104) => metadata104.tags.includes("alternative-me")
|
|
54170
55131
|
},
|
|
54171
55132
|
{
|
|
54172
55133
|
id: "blockchain",
|
|
@@ -54175,7 +55136,16 @@ var PROVIDER_CATALOG = [
|
|
|
54175
55136
|
type: "Bitcoin on-chain charts: hash rate, active addresses, transaction count, market price, difficulty, fees, miner revenue (keyless)",
|
|
54176
55137
|
clientStrategy: "internal",
|
|
54177
55138
|
alwaysAvailable: true,
|
|
54178
|
-
matches: (_toolName,
|
|
55139
|
+
matches: (_toolName, metadata104) => metadata104.tags.includes("blockchain-com")
|
|
55140
|
+
},
|
|
55141
|
+
{
|
|
55142
|
+
id: "blockscout",
|
|
55143
|
+
name: "Blockscout",
|
|
55144
|
+
prefix: "blockscout_",
|
|
55145
|
+
type: "EVM token holder lists and counts from Blockscout explorers (keyless public API; BLOCKSCOUT_API_KEY switches to the PRO API)",
|
|
55146
|
+
clientStrategy: "internal",
|
|
55147
|
+
alwaysAvailable: true,
|
|
55148
|
+
matches: (_toolName, metadata104) => metadata104.tags.includes("blockscout")
|
|
54179
55149
|
},
|
|
54180
55150
|
{
|
|
54181
55151
|
id: "fetch",
|
|
@@ -54184,7 +55154,7 @@ var PROVIDER_CATALOG = [
|
|
|
54184
55154
|
type: "Allowlisted fetch of long-tail public crypto APIs (the escape hatch when no typed tool covers a source)",
|
|
54185
55155
|
clientStrategy: "internal",
|
|
54186
55156
|
alwaysAvailable: true,
|
|
54187
|
-
matches: (toolName,
|
|
55157
|
+
matches: (toolName, metadata104) => matchesProvider("fetch", toolName, metadata104)
|
|
54188
55158
|
},
|
|
54189
55159
|
{
|
|
54190
55160
|
id: "tenderly",
|
|
@@ -54196,7 +55166,7 @@ var PROVIDER_CATALOG = [
|
|
|
54196
55166
|
label: "Tenderly REST credentials or TENDERLY_NODE_ACCESS_KEY/TENDERLY_GATEWAY_ACCESS_KEY",
|
|
54197
55167
|
isConfigured: () => hasTenderlyRestCredentials() || hasTenderlyNodeCredentials()
|
|
54198
55168
|
},
|
|
54199
|
-
matches: (toolName,
|
|
55169
|
+
matches: (toolName, metadata104) => matchesProvider("tenderly", toolName, metadata104)
|
|
54200
55170
|
},
|
|
54201
55171
|
{
|
|
54202
55172
|
id: "moralis",
|
|
@@ -54208,7 +55178,7 @@ var PROVIDER_CATALOG = [
|
|
|
54208
55178
|
label: "MORALIS_API_KEY",
|
|
54209
55179
|
isConfigured: () => hasEnvVar("MORALIS_API_KEY")
|
|
54210
55180
|
},
|
|
54211
|
-
matches: (toolName,
|
|
55181
|
+
matches: (toolName, metadata104) => matchesProvider("moralis", toolName, metadata104)
|
|
54212
55182
|
},
|
|
54213
55183
|
{
|
|
54214
55184
|
id: "helius",
|
|
@@ -54220,7 +55190,7 @@ var PROVIDER_CATALOG = [
|
|
|
54220
55190
|
label: "HELIUS_API_KEY",
|
|
54221
55191
|
isConfigured: () => hasEnvVar("HELIUS_API_KEY")
|
|
54222
55192
|
},
|
|
54223
|
-
matches: (toolName,
|
|
55193
|
+
matches: (toolName, metadata104) => matchesProvider("helius", toolName, metadata104)
|
|
54224
55194
|
},
|
|
54225
55195
|
{
|
|
54226
55196
|
id: "alchemy",
|
|
@@ -54232,11 +55202,11 @@ var PROVIDER_CATALOG = [
|
|
|
54232
55202
|
label: "ALCHEMY_API_KEY",
|
|
54233
55203
|
isConfigured: () => hasEnvVar("ALCHEMY_API_KEY")
|
|
54234
55204
|
},
|
|
54235
|
-
matches: (toolName,
|
|
55205
|
+
matches: (toolName, metadata104) => matchesProvider("alchemy", toolName, metadata104)
|
|
54236
55206
|
}
|
|
54237
55207
|
];
|
|
54238
|
-
function getProviderDescriptorForTool(toolName,
|
|
54239
|
-
return PROVIDER_CATALOG.find((provider) => provider.matches(toolName,
|
|
55208
|
+
function getProviderDescriptorForTool(toolName, metadata104) {
|
|
55209
|
+
return PROVIDER_CATALOG.find((provider) => provider.matches(toolName, metadata104)) ?? UNKNOWN_PROVIDER_DESCRIPTOR;
|
|
54240
55210
|
}
|
|
54241
55211
|
function getProviderDescriptorForEndpoint(endpoint) {
|
|
54242
55212
|
return getProviderDescriptorForTool(endpoint.tool.name, endpoint.metadata);
|
|
@@ -54265,7 +55235,7 @@ function getProviderStatusCatalog() {
|
|
|
54265
55235
|
})
|
|
54266
55236
|
);
|
|
54267
55237
|
}
|
|
54268
|
-
function getCredentialRequirementForTool(toolName,
|
|
55238
|
+
function getCredentialRequirementForTool(toolName, metadata104) {
|
|
54269
55239
|
if (toolName === "tenderly_get_supported_networks" || toolName === "tenderly_get_block_number") {
|
|
54270
55240
|
return null;
|
|
54271
55241
|
}
|
|
@@ -54278,7 +55248,7 @@ function getCredentialRequirementForTool(toolName, metadata100) {
|
|
|
54278
55248
|
isConfigured: hasTenderlyNodeCredentials
|
|
54279
55249
|
};
|
|
54280
55250
|
}
|
|
54281
|
-
return getProviderDescriptorForTool(toolName,
|
|
55251
|
+
return getProviderDescriptorForTool(toolName, metadata104).credentialRequirement ?? null;
|
|
54282
55252
|
}
|
|
54283
55253
|
|
|
54284
55254
|
// src/mcpInvokers.ts
|
|
@@ -54309,14 +55279,14 @@ var RESPONSE_FORMAT_SCHEMA = {
|
|
|
54309
55279
|
enum: ["json", "markdown"],
|
|
54310
55280
|
description: "Output format: 'json' for structured data (default) or 'markdown' for human-readable text."
|
|
54311
55281
|
};
|
|
54312
|
-
function normalizeToolSchema(
|
|
55282
|
+
function normalizeToolSchema(tool100) {
|
|
54313
55283
|
const normalized = {
|
|
54314
|
-
...
|
|
54315
|
-
inputSchema:
|
|
54316
|
-
title:
|
|
55284
|
+
...tool100,
|
|
55285
|
+
inputSchema: tool100.inputSchema ? JSON.parse(JSON.stringify(tool100.inputSchema)) : tool100.inputSchema,
|
|
55286
|
+
title: tool100.title ?? getToolTitle(tool100.name),
|
|
54317
55287
|
annotations: {
|
|
54318
|
-
...getAnnotationsForTool(
|
|
54319
|
-
...
|
|
55288
|
+
...getAnnotationsForTool(tool100.name),
|
|
55289
|
+
...tool100.annotations ?? {}
|
|
54320
55290
|
}
|
|
54321
55291
|
};
|
|
54322
55292
|
if (normalized.inputSchema && typeof normalized.inputSchema === "object" && normalized.inputSchema.type === "object") {
|
|
@@ -54343,20 +55313,19 @@ function normalizeEndpointSchema(endpoint) {
|
|
|
54343
55313
|
var RETIRED_TOOLS_DATE = "2026-09-06";
|
|
54344
55314
|
var RETIRED_TOOL_REPLACEMENTS = {
|
|
54345
55315
|
// wallet
|
|
54346
|
-
codex_filter_wallets_by_network: "Use
|
|
54347
|
-
codex_get_wallet_aggregate_backfill_state: "Use
|
|
54348
|
-
codex_get_wallet_label_types: "Use
|
|
54349
|
-
filter_network_wallets: "Use
|
|
54350
|
-
filter_token_wallets: "Use
|
|
54351
|
-
filter_wallets: "Use
|
|
54352
|
-
|
|
54353
|
-
|
|
54354
|
-
|
|
54355
|
-
get_wallet_token_events: "Use alchemy_get_token_balances_by_wallet, alchemy_get_asset_transfers, or moralis_get_wallet_profitability_summary for wallet balances, transfers, and PnL.",
|
|
55316
|
+
codex_filter_wallets_by_network: "Use get_wallet_balances, get_wallet_transfers, get_wallet_trades, or get_wallet_pnl for wallet balances, transfers, DEX trades, and PnL.",
|
|
55317
|
+
codex_get_wallet_aggregate_backfill_state: "Use get_wallet_balances, get_wallet_transfers, get_wallet_trades, or get_wallet_pnl for wallet balances, transfers, DEX trades, and PnL.",
|
|
55318
|
+
codex_get_wallet_label_types: "Use get_wallet_balances, get_wallet_transfers, get_wallet_trades, or get_wallet_pnl for wallet balances, transfers, DEX trades, and PnL.",
|
|
55319
|
+
filter_network_wallets: "Use get_wallet_balances, get_wallet_transfers, get_wallet_trades, or get_wallet_pnl for wallet balances, transfers, DEX trades, and PnL.",
|
|
55320
|
+
filter_token_wallets: "Use get_wallet_balances, get_wallet_transfers, get_wallet_trades, or get_wallet_pnl for wallet balances, transfers, DEX trades, and PnL.",
|
|
55321
|
+
filter_wallets: "Use get_wallet_balances, get_wallet_transfers, get_wallet_trades, or get_wallet_pnl for wallet balances, transfers, DEX trades, and PnL.",
|
|
55322
|
+
get_wallet_chart: "Use get_wallet_balances, get_wallet_transfers, get_wallet_trades, or get_wallet_pnl for wallet balances, transfers, DEX trades, and PnL.",
|
|
55323
|
+
get_wallet_stats: "Use get_wallet_balances, get_wallet_transfers, get_wallet_trades, or get_wallet_pnl for wallet balances, transfers, DEX trades, and PnL.",
|
|
55324
|
+
get_wallet_token_events: "Use get_wallet_balances, get_wallet_transfers, get_wallet_trades, or get_wallet_pnl for wallet balances, transfers, DEX trades, and PnL.",
|
|
54356
55325
|
// holders
|
|
54357
|
-
get_token_holders: "Use get_token_top_holders for holders on any network, coingecko_get_onchain_token_top_traders for traders
|
|
54358
|
-
get_token_top_traders: "Use get_token_top_holders for holders on any network, coingecko_get_onchain_token_top_traders for traders
|
|
54359
|
-
get_top_holders_percentage: "Use get_token_top_holders for holders on any network, coingecko_get_onchain_token_top_traders for traders
|
|
55326
|
+
get_token_holders: "Use get_token_top_holders for holders on any network, or coingecko_get_onchain_token_top_traders for traders (tokens created after 2023-09-01; not wrapped native tokens or stablecoins).",
|
|
55327
|
+
get_token_top_traders: "Use get_token_top_holders for holders on any network, or coingecko_get_onchain_token_top_traders for traders (tokens created after 2023-09-01; not wrapped native tokens or stablecoins).",
|
|
55328
|
+
get_top_holders_percentage: "Use get_token_top_holders for holders on any network, or coingecko_get_onchain_token_top_traders for traders (tokens created after 2023-09-01; not wrapped native tokens or stablecoins).",
|
|
54360
55329
|
// pairs
|
|
54361
55330
|
codex_get_detailed_token_stats: "Use get_token_pools, get_pool_info, get_pools_by_address, moralis_get_pair_stats, or get_new_pools for pool and pair data.",
|
|
54362
55331
|
codex_get_windowed_pair_stats: "Use get_token_pools, get_pool_info, get_pools_by_address, moralis_get_pair_stats, or get_new_pools for pool and pair data.",
|
|
@@ -54414,7 +55383,12 @@ var HELIUS_WALLET_PLAN = "because Helius answers 403 for it on Hive's current He
|
|
|
54414
55383
|
var DEFILLAMA_PRO = "because it needs a DeFiLlama Pro plan, which Hive does not have";
|
|
54415
55384
|
var DEFILLAMA_DERIVATIVES_REPLACEMENT = "Use defillama_get_open_interest, defillama_get_derivatives_aggregator_volumes, or get_derivatives_exchanges for derivatives activity.";
|
|
54416
55385
|
var ALCHEMY_PAYG = "because Alchemy offers it only on Pay As You Go and Enterprise plans, and Hive's key is on the Free tier";
|
|
54417
|
-
var ALCHEMY_BUNDLE_REPLACEMENT = "Use tenderly_simulate_bundle for multi-transaction simulation
|
|
55386
|
+
var ALCHEMY_BUNDLE_REPLACEMENT = "Use tenderly_simulate_bundle or tenderly_node_simulate_bundle for multi-transaction simulation.";
|
|
55387
|
+
var ALCHEMY_SIMULATION_SUNSET = 'because Alchemy shut down its Transaction Simulation API on 2026-09-30 and now answers "Unsupported method" on every plan';
|
|
55388
|
+
var ALCHEMY_SIMULATION_REPLACEMENT = "Use tenderly_simulate_transaction or tenderly_node_simulate_transaction; both return the execution trace, logs, and asset and balance changes without broadcasting.";
|
|
55389
|
+
var MORALIS_PAUSED = "because Hive's Moralis account is paused and will not be renewed";
|
|
55390
|
+
var MORALIS_BALANCES_REPLACEMENT = "Use get_wallet_balances for priced EVM balances across chains.";
|
|
55391
|
+
var MORALIS_PNL_REPLACEMENT = "Use get_wallet_pnl for realized and unrealized PnL per wallet and per token.";
|
|
54418
55392
|
var CCXT_PRIVATE = "because every exchange that serves it requires an authenticated exchange account";
|
|
54419
55393
|
var CCXT_BORROW_REPLACEMENT = "Use ccxt_fetch_borrow_rate_history or ccxt_fetch_borrow_rate_histories (public) and read the latest point.";
|
|
54420
55394
|
var UNSERVABLE_TOOLS = {
|
|
@@ -54434,10 +55408,6 @@ var UNSERVABLE_TOOLS = {
|
|
|
54434
55408
|
reason: COINGECKO_ENTERPRISE,
|
|
54435
55409
|
replacement: COINGECKO_SUPPLY_REPLACEMENT
|
|
54436
55410
|
},
|
|
54437
|
-
get_coin_insights: {
|
|
54438
|
-
reason: COINGECKO_ENTERPRISE,
|
|
54439
|
-
replacement: "No direct replacement; use get_coins_market_data and get_coin_tickers for the market facts behind an insight."
|
|
54440
|
-
},
|
|
54441
55411
|
helius_get_wallet_identity: {
|
|
54442
55412
|
reason: HELIUS_WALLET_PLAN,
|
|
54443
55413
|
replacement: "No other wallet-identity source is live on Hive's plans; use helius_get_wallet_history or helius_get_transfers_by_address for the wallet's activity."
|
|
@@ -54498,9 +55468,74 @@ var UNSERVABLE_TOOLS = {
|
|
|
54498
55468
|
reason: CCXT_PRIVATE,
|
|
54499
55469
|
replacement: CCXT_BORROW_REPLACEMENT
|
|
54500
55470
|
},
|
|
55471
|
+
alchemy_simulate_asset_changes: {
|
|
55472
|
+
reason: ALCHEMY_SIMULATION_SUNSET,
|
|
55473
|
+
replacement: ALCHEMY_SIMULATION_REPLACEMENT,
|
|
55474
|
+
date: "2026-10-09"
|
|
55475
|
+
},
|
|
55476
|
+
alchemy_simulate_execution: {
|
|
55477
|
+
reason: ALCHEMY_SIMULATION_SUNSET,
|
|
55478
|
+
replacement: ALCHEMY_SIMULATION_REPLACEMENT,
|
|
55479
|
+
date: "2026-10-09"
|
|
55480
|
+
},
|
|
55481
|
+
alchemy_is_holder_of_contract: {
|
|
55482
|
+
reason: 'because Alchemy deprecated the isHolderOfContract endpoint, which now answers 404 "This API endpoint has been deprecated"',
|
|
55483
|
+
replacement: "Use alchemy_get_nfts_for_owner for the wallet and look for the contract, or alchemy_get_owners_for_contract and look for the wallet.",
|
|
55484
|
+
date: "2026-10-09"
|
|
55485
|
+
},
|
|
54501
55486
|
goplus_simulate_solana_transaction: {
|
|
54502
55487
|
reason: 'because GoPlus answers "system error" to every Solana pre-execution request, including a fresh valid transfer',
|
|
54503
55488
|
replacement: "Use get_solana_token_security for the tokens the transaction touches; Hive has no other Solana pre-signing simulation."
|
|
55489
|
+
},
|
|
55490
|
+
moralis_get_wallet_token_balances: {
|
|
55491
|
+
reason: MORALIS_PAUSED,
|
|
55492
|
+
replacement: MORALIS_BALANCES_REPLACEMENT,
|
|
55493
|
+
date: "2026-10-14"
|
|
55494
|
+
},
|
|
55495
|
+
moralis_get_wallet_token_balances_price: {
|
|
55496
|
+
reason: MORALIS_PAUSED,
|
|
55497
|
+
replacement: MORALIS_BALANCES_REPLACEMENT,
|
|
55498
|
+
date: "2026-10-14"
|
|
55499
|
+
},
|
|
55500
|
+
moralis_get_wallet_net_worth: {
|
|
55501
|
+
reason: MORALIS_PAUSED,
|
|
55502
|
+
replacement: MORALIS_BALANCES_REPLACEMENT,
|
|
55503
|
+
date: "2026-10-14"
|
|
55504
|
+
},
|
|
55505
|
+
moralis_get_wallet_profitability: {
|
|
55506
|
+
reason: MORALIS_PAUSED,
|
|
55507
|
+
replacement: MORALIS_PNL_REPLACEMENT,
|
|
55508
|
+
date: "2026-10-14"
|
|
55509
|
+
},
|
|
55510
|
+
moralis_get_wallet_profitability_summary: {
|
|
55511
|
+
reason: MORALIS_PAUSED,
|
|
55512
|
+
replacement: MORALIS_PNL_REPLACEMENT,
|
|
55513
|
+
date: "2026-10-14"
|
|
55514
|
+
},
|
|
55515
|
+
moralis_get_wallet_pnl: {
|
|
55516
|
+
reason: MORALIS_PAUSED,
|
|
55517
|
+
replacement: MORALIS_PNL_REPLACEMENT,
|
|
55518
|
+
date: "2026-10-14"
|
|
55519
|
+
},
|
|
55520
|
+
moralis_get_wallet_pnl_summary: {
|
|
55521
|
+
reason: MORALIS_PAUSED,
|
|
55522
|
+
replacement: MORALIS_PNL_REPLACEMENT,
|
|
55523
|
+
date: "2026-10-14"
|
|
55524
|
+
},
|
|
55525
|
+
moralis_get_wallet_swaps: {
|
|
55526
|
+
reason: MORALIS_PAUSED,
|
|
55527
|
+
replacement: "Use get_wallet_trades for a wallet's DEX buys and sells.",
|
|
55528
|
+
date: "2026-10-14"
|
|
55529
|
+
},
|
|
55530
|
+
moralis_get_wallet_token_transfers: {
|
|
55531
|
+
reason: MORALIS_PAUSED,
|
|
55532
|
+
replacement: "Use get_wallet_transfers for EVM token transfers, or helius_get_wallet_transfers on Solana.",
|
|
55533
|
+
date: "2026-10-14"
|
|
55534
|
+
},
|
|
55535
|
+
moralis_get_token_top_traders: {
|
|
55536
|
+
reason: MORALIS_PAUSED,
|
|
55537
|
+
replacement: "Use coingecko_get_onchain_token_top_traders for a token's most profitable traders.",
|
|
55538
|
+
date: "2026-10-14"
|
|
54504
55539
|
}
|
|
54505
55540
|
};
|
|
54506
55541
|
function isUnservableTool(toolName) {
|
|
@@ -54510,7 +55545,7 @@ function getRetiredTool(toolName) {
|
|
|
54510
55545
|
if (isUnservableTool(toolName)) {
|
|
54511
55546
|
const entry2 = UNSERVABLE_TOOLS[toolName];
|
|
54512
55547
|
return {
|
|
54513
|
-
date: UNSERVABLE_TOOLS_DATE,
|
|
55548
|
+
date: entry2.date ?? UNSERVABLE_TOOLS_DATE,
|
|
54514
55549
|
cause: entry2.reason,
|
|
54515
55550
|
replacement: entry2.replacement
|
|
54516
55551
|
};
|
|
@@ -54687,7 +55722,40 @@ function boundedJsonTruncationStub(originalLength, note, characterLimit) {
|
|
|
54687
55722
|
];
|
|
54688
55723
|
return candidates.find((candidate) => candidate.length <= characterLimit) ?? "";
|
|
54689
55724
|
}
|
|
54690
|
-
function
|
|
55725
|
+
function inputSchemaParamNames(schema) {
|
|
55726
|
+
const properties = schema?.properties;
|
|
55727
|
+
return properties && typeof properties === "object" ? Object.keys(properties) : [];
|
|
55728
|
+
}
|
|
55729
|
+
function truncationRecallHint(params = [], nextOffset) {
|
|
55730
|
+
const names = new Set(params);
|
|
55731
|
+
if (names.has("offset")) {
|
|
55732
|
+
return `Re-call with offset=${nextOffset ?? "_pagination.next_offset"} to continue, or add filters.`;
|
|
55733
|
+
}
|
|
55734
|
+
const sizeParam = ["limit", "per_page", "page_size", "count"].find(
|
|
55735
|
+
(name) => names.has(name)
|
|
55736
|
+
);
|
|
55737
|
+
return sizeParam ? `This tool has no offset parameter: a smaller ${sizeParam} returns an untruncated page; narrower filters reach the remaining rows.` : "This tool has no paging parameter: narrow the request with its filters to see the rest.";
|
|
55738
|
+
}
|
|
55739
|
+
function truncationPagination(kept, original, context) {
|
|
55740
|
+
const params = [...context.params ?? []];
|
|
55741
|
+
const head = `Response truncated from ${original} to ${kept} items to fit the response budget.`;
|
|
55742
|
+
const base = {
|
|
55743
|
+
count: kept,
|
|
55744
|
+
total_count: original,
|
|
55745
|
+
has_more: kept < original
|
|
55746
|
+
};
|
|
55747
|
+
if (!params.includes("offset")) {
|
|
55748
|
+
return { ...base, note: `${head} ${truncationRecallHint(params)}` };
|
|
55749
|
+
}
|
|
55750
|
+
const start = typeof context.offset === "number" && Number.isFinite(context.offset) ? Math.max(0, Math.floor(context.offset)) : 0;
|
|
55751
|
+
return {
|
|
55752
|
+
...base,
|
|
55753
|
+
offset: start,
|
|
55754
|
+
next_offset: start + kept,
|
|
55755
|
+
note: `${head} ${truncationRecallHint(params, start + kept)}`
|
|
55756
|
+
};
|
|
55757
|
+
}
|
|
55758
|
+
function smartTruncate(text, characterLimit = CHARACTER_LIMIT, pagination = {}) {
|
|
54691
55759
|
if (text.length <= characterLimit) return text;
|
|
54692
55760
|
const originalLength = text.length;
|
|
54693
55761
|
const head = text.trimStart();
|
|
@@ -54748,14 +55816,11 @@ function smartTruncate(text, characterLimit = CHARACTER_LIMIT) {
|
|
|
54748
55816
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
54749
55817
|
const obj = parsed;
|
|
54750
55818
|
if (pagInfo && pagInfo.kept < pagInfo.original) {
|
|
54751
|
-
obj._pagination =
|
|
54752
|
-
|
|
54753
|
-
|
|
54754
|
-
|
|
54755
|
-
|
|
54756
|
-
next_offset: pagInfo.kept,
|
|
54757
|
-
note: `Response truncated from ${pagInfo.original} to ${pagInfo.kept} items. Re-call with offset=${pagInfo.kept} or add filters to see more.`
|
|
54758
|
-
};
|
|
55819
|
+
obj._pagination = truncationPagination(
|
|
55820
|
+
pagInfo.kept,
|
|
55821
|
+
pagInfo.original,
|
|
55822
|
+
pagination
|
|
55823
|
+
);
|
|
54759
55824
|
}
|
|
54760
55825
|
if (dropped.length > 0) {
|
|
54761
55826
|
obj._dropped_fields = dropped;
|
|
@@ -54826,9 +55891,9 @@ function withExecMarker(result2, marker) {
|
|
|
54826
55891
|
function humanizePropertyName(propName) {
|
|
54827
55892
|
return propName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim().toLowerCase();
|
|
54828
55893
|
}
|
|
54829
|
-
function fillMissingInputDescriptions(
|
|
54830
|
-
const schema =
|
|
54831
|
-
if (!schema?.properties) return
|
|
55894
|
+
function fillMissingInputDescriptions(tool100) {
|
|
55895
|
+
const schema = tool100.inputSchema;
|
|
55896
|
+
if (!schema?.properties) return tool100;
|
|
54832
55897
|
let changed = false;
|
|
54833
55898
|
const properties = {};
|
|
54834
55899
|
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
@@ -54839,14 +55904,14 @@ function fillMissingInputDescriptions(tool96) {
|
|
|
54839
55904
|
changed = true;
|
|
54840
55905
|
properties[propName] = {
|
|
54841
55906
|
...propSchema,
|
|
54842
|
-
description: `${humanizePropertyName(propName)} parameter for ${
|
|
55907
|
+
description: `${humanizePropertyName(propName)} parameter for ${tool100.name}. See the tool Args section for accepted values and examples.`
|
|
54843
55908
|
};
|
|
54844
55909
|
}
|
|
54845
|
-
if (!changed) return
|
|
55910
|
+
if (!changed) return tool100;
|
|
54846
55911
|
return {
|
|
54847
|
-
...
|
|
55912
|
+
...tool100,
|
|
54848
55913
|
inputSchema: {
|
|
54849
|
-
...
|
|
55914
|
+
...tool100.inputSchema,
|
|
54850
55915
|
properties
|
|
54851
55916
|
}
|
|
54852
55917
|
};
|
|
@@ -54872,24 +55937,25 @@ function stripProviderPrefix(toolName) {
|
|
|
54872
55937
|
var fallbackCoinGeckoClient = null;
|
|
54873
55938
|
function getFallbackCoinGeckoClient() {
|
|
54874
55939
|
if (!fallbackCoinGeckoClient) {
|
|
54875
|
-
fallbackCoinGeckoClient = new
|
|
55940
|
+
fallbackCoinGeckoClient = new Coingecko3(getCoinGeckoClientOptions());
|
|
54876
55941
|
}
|
|
54877
55942
|
return fallbackCoinGeckoClient;
|
|
54878
55943
|
}
|
|
54879
55944
|
function isKeylessCoinGeckoClient(client2) {
|
|
54880
|
-
return client2 instanceof
|
|
55945
|
+
return client2 instanceof Coingecko3 && !client2.proAPIKey && !client2.demoAPIKey;
|
|
54881
55946
|
}
|
|
54882
|
-
function createSafeHandler(
|
|
55947
|
+
function createSafeHandler(handler100, metadata104, toolName, inputSchema) {
|
|
55948
|
+
const schemaParams = inputSchemaParamNames(inputSchema);
|
|
54883
55949
|
const credentialRequirement = getCredentialRequirementForTool(
|
|
54884
55950
|
toolName,
|
|
54885
|
-
|
|
55951
|
+
metadata104
|
|
54886
55952
|
);
|
|
54887
55953
|
return async (client2, args) => {
|
|
54888
55954
|
const t0 = Date.now();
|
|
54889
55955
|
try {
|
|
54890
|
-
let usedFallback =
|
|
55956
|
+
let usedFallback = metadata104.tags.includes("coingecko") && isKeylessCoinGeckoClient(client2);
|
|
54891
55957
|
let effectiveClient = client2;
|
|
54892
|
-
if (effectiveClient == null &&
|
|
55958
|
+
if (effectiveClient == null && metadata104.tags.includes("coingecko")) {
|
|
54893
55959
|
effectiveClient = getFallbackCoinGeckoClient();
|
|
54894
55960
|
usedFallback = isKeylessCoinGeckoClient(effectiveClient);
|
|
54895
55961
|
}
|
|
@@ -54920,7 +55986,7 @@ function createSafeHandler(handler96, metadata100, toolName) {
|
|
|
54920
55986
|
);
|
|
54921
55987
|
}
|
|
54922
55988
|
}
|
|
54923
|
-
let result2 = await
|
|
55989
|
+
let result2 = await handler100(effectiveClient, safeArgs);
|
|
54924
55990
|
const observedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
54925
55991
|
if (result2._hiveExec?.source === "fallback") usedFallback = true;
|
|
54926
55992
|
if (!result2.isError && credentialRequirement && !credentialRequirement.isConfigured() && result2?.content?.[0]?.type === "text" && result2.content[0].text) {
|
|
@@ -54975,7 +56041,15 @@ function createSafeHandler(handler96, metadata100, toolName) {
|
|
|
54975
56041
|
withExecMarker(
|
|
54976
56042
|
{
|
|
54977
56043
|
...result2,
|
|
54978
|
-
content: [
|
|
56044
|
+
content: [
|
|
56045
|
+
{
|
|
56046
|
+
type: "text",
|
|
56047
|
+
text: smartTruncate(text, CHARACTER_LIMIT, {
|
|
56048
|
+
params: schemaParams,
|
|
56049
|
+
offset: safeArgs.offset
|
|
56050
|
+
})
|
|
56051
|
+
}
|
|
56052
|
+
]
|
|
54979
56053
|
},
|
|
54980
56054
|
{
|
|
54981
56055
|
truncated: true,
|
|
@@ -55138,14 +56212,15 @@ function getAllWrappedEndpoints() {
|
|
|
55138
56212
|
...getAllArchiveEndpoints(),
|
|
55139
56213
|
...getAllPolymarketEndpoints(),
|
|
55140
56214
|
...getAllSignalsEndpoints(),
|
|
56215
|
+
...getAllBlockscoutEndpoints(),
|
|
55141
56216
|
...getAllFetchEndpoints()
|
|
55142
56217
|
];
|
|
55143
56218
|
const allEndpointsJSON = wrapper;
|
|
55144
56219
|
const updatedTools = [];
|
|
55145
56220
|
for (const endpoint of allTool) {
|
|
55146
|
-
let { tool:
|
|
56221
|
+
let { tool: tool100 } = endpoint;
|
|
55147
56222
|
let matchedItem = null;
|
|
55148
|
-
const originalName =
|
|
56223
|
+
const originalName = tool100.name;
|
|
55149
56224
|
const strippedName = stripProviderPrefix(originalName);
|
|
55150
56225
|
for (const endpoints of Object.values(allEndpointsJSON)) {
|
|
55151
56226
|
for (const item of endpoints) {
|
|
@@ -55171,10 +56246,10 @@ function getAllWrappedEndpoints() {
|
|
|
55171
56246
|
}
|
|
55172
56247
|
if (matchedItem) {
|
|
55173
56248
|
let toolInstanse = {
|
|
55174
|
-
...
|
|
56249
|
+
...tool100,
|
|
55175
56250
|
name: matchedItem.tool.name,
|
|
55176
|
-
title:
|
|
55177
|
-
description: matchedItem.tool.description ??
|
|
56251
|
+
title: tool100.title ?? getToolTitle(matchedItem.tool.name),
|
|
56252
|
+
description: matchedItem.tool.description ?? tool100.description
|
|
55178
56253
|
};
|
|
55179
56254
|
if (!toolInstanse.annotations) {
|
|
55180
56255
|
toolInstanse.annotations = getAnnotationsForTool(toolInstanse.name);
|
|
@@ -55193,7 +56268,8 @@ function getAllWrappedEndpoints() {
|
|
|
55193
56268
|
handler: createSafeHandler(
|
|
55194
56269
|
endpoint.handler,
|
|
55195
56270
|
endpoint.metadata,
|
|
55196
|
-
toolInstanse.name
|
|
56271
|
+
toolInstanse.name,
|
|
56272
|
+
toolInstanse.inputSchema
|
|
55197
56273
|
),
|
|
55198
56274
|
metadata: endpoint.metadata,
|
|
55199
56275
|
tool: toolInstanse
|
|
@@ -55216,10 +56292,10 @@ function getAllWrappedEndpoints() {
|
|
|
55216
56292
|
"signals_",
|
|
55217
56293
|
"fetch_"
|
|
55218
56294
|
];
|
|
55219
|
-
if (selfDescribedPrefixes.some((p) =>
|
|
56295
|
+
if (selfDescribedPrefixes.some((p) => tool100.name.startsWith(p)) || isFreeWriteEndpoint(tool100.name)) {
|
|
55220
56296
|
let toolInstanse = {
|
|
55221
|
-
...
|
|
55222
|
-
title:
|
|
56297
|
+
...tool100,
|
|
56298
|
+
title: tool100.title ?? getToolTitle(tool100.name)
|
|
55223
56299
|
};
|
|
55224
56300
|
if (!toolInstanse.annotations) {
|
|
55225
56301
|
toolInstanse.annotations = getAnnotationsForTool(toolInstanse.name);
|
|
@@ -55237,14 +56313,15 @@ function getAllWrappedEndpoints() {
|
|
|
55237
56313
|
handler: createSafeHandler(
|
|
55238
56314
|
endpoint.handler,
|
|
55239
56315
|
endpoint.metadata,
|
|
55240
|
-
toolInstanse.name
|
|
56316
|
+
toolInstanse.name,
|
|
56317
|
+
toolInstanse.inputSchema
|
|
55241
56318
|
),
|
|
55242
56319
|
metadata: endpoint.metadata,
|
|
55243
56320
|
tool: toolInstanse
|
|
55244
56321
|
});
|
|
55245
56322
|
} else {
|
|
55246
56323
|
secureLogger.debug(
|
|
55247
|
-
`No match found in allEndpoints for tool: ${
|
|
56324
|
+
`No match found in allEndpoints for tool: ${tool100.name}`
|
|
55248
56325
|
);
|
|
55249
56326
|
}
|
|
55250
56327
|
}
|
|
@@ -55260,9 +56337,9 @@ function getToolByCategory(category) {
|
|
|
55260
56337
|
const allWrappedTools = getAllWrappedEndpoints();
|
|
55261
56338
|
const tools5 = [];
|
|
55262
56339
|
for (const toolName of toolNameList) {
|
|
55263
|
-
const
|
|
55264
|
-
if (
|
|
55265
|
-
tools5.push(
|
|
56340
|
+
const tool100 = allWrappedTools.find((tool101) => tool101.tool.name === toolName);
|
|
56341
|
+
if (tool100) {
|
|
56342
|
+
tools5.push(tool100);
|
|
55266
56343
|
}
|
|
55267
56344
|
}
|
|
55268
56345
|
return tools5;
|
|
@@ -55485,8 +56562,13 @@ var ToolRegistry = [
|
|
|
55485
56562
|
{
|
|
55486
56563
|
category: "Portfolio & Wallet",
|
|
55487
56564
|
name: "get_portfolio_wallet_endpoints",
|
|
55488
|
-
description: "Endpoints for tracking user wallet balances, transaction history, portfolio positions across protocols and chains, net asset calculations, token holdings analysis, comprehensive wallet activity monitoring, real-time balance tracking, balance update history over time, multi-token portfolio analysis, balance changes with transaction context, financial auditing capabilities for individual users and addresses, wallet filtering and discovery, NFT holdings tracking, wallet PnL, DeFi positions, swap history, and wallet insight metrics. Provider guide: Use
|
|
56565
|
+
description: "Endpoints for tracking user wallet balances, transaction history, portfolio positions across protocols and chains, net asset calculations, token holdings analysis, comprehensive wallet activity monitoring, real-time balance tracking, balance update history over time, multi-token portfolio analysis, balance changes with transaction context, financial auditing capabilities for individual users and addresses, wallet filtering and discovery, NFT holdings tracking, wallet PnL, DeFi positions, swap history, and wallet insight metrics. Provider guide: Use get_wallet_balances, get_wallet_pnl, get_wallet_trades, and get_wallet_transfers (CoinGecko) for EVM balances, PnL, DEX trades, and transfers. Use alchemy_ tools for NFT holdings, raw transfers, and EVM chains CoinGecko does not cover. Use moralis_ tools only for DeFi positions and decoded wallet insight/history. Use helius_ tools for Solana-native wallet depth (native SOL balance, SPL token accounts, Digital Asset Standard assets owned by a wallet).",
|
|
55489
56566
|
tools: [
|
|
56567
|
+
// CoinGecko wallet data (default for EVM balances, PnL, DEX trades, transfers)
|
|
56568
|
+
"get_wallet_balances",
|
|
56569
|
+
"get_wallet_pnl",
|
|
56570
|
+
"get_wallet_trades",
|
|
56571
|
+
"get_wallet_transfers",
|
|
55490
56572
|
"alchemy_get_tokens_by_wallet",
|
|
55491
56573
|
"alchemy_get_token_balances_by_wallet",
|
|
55492
56574
|
"alchemy_get_nfts_by_wallet",
|
|
@@ -55517,24 +56599,15 @@ var ToolRegistry = [
|
|
|
55517
56599
|
"alchemy_solana_get_transaction_with_compression_info",
|
|
55518
56600
|
// Moralis wallet intelligence tools
|
|
55519
56601
|
"moralis_get_wallet_history",
|
|
55520
|
-
"moralis_get_wallet_profitability",
|
|
55521
|
-
"moralis_get_wallet_profitability_summary",
|
|
55522
|
-
"moralis_get_wallet_pnl",
|
|
55523
|
-
"moralis_get_wallet_pnl_summary",
|
|
55524
56602
|
"moralis_get_wallet_defi_positions",
|
|
55525
56603
|
"moralis_get_wallet_defi_summary",
|
|
55526
|
-
"moralis_get_wallet_swaps",
|
|
55527
56604
|
"moralis_get_wallet_chain_activity",
|
|
55528
56605
|
"moralis_get_wallet_stats",
|
|
55529
56606
|
"moralis_get_wallet_insight",
|
|
55530
|
-
"moralis_get_wallet_token_balances",
|
|
55531
|
-
"moralis_get_wallet_token_balances_price",
|
|
55532
|
-
"moralis_get_wallet_net_worth",
|
|
55533
56607
|
"moralis_get_wallet_approvals",
|
|
55534
56608
|
"moralis_get_wallet_active_chains",
|
|
55535
56609
|
"moralis_get_native_balance",
|
|
55536
56610
|
"moralis_get_native_balances_batch",
|
|
55537
|
-
"moralis_get_wallet_token_transfers",
|
|
55538
56611
|
"moralis_get_sol_balance",
|
|
55539
56612
|
"moralis_get_sol_token_balances",
|
|
55540
56613
|
"moralis_get_sol_portfolio",
|
|
@@ -55594,6 +56667,7 @@ var ToolRegistry = [
|
|
|
55594
56667
|
"get_tokens_by_address",
|
|
55595
56668
|
"get_token_info",
|
|
55596
56669
|
"get_token_top_holders",
|
|
56670
|
+
"get_token_holder_distribution",
|
|
55597
56671
|
"get_token_holders_chart",
|
|
55598
56672
|
"get_multi_tokens",
|
|
55599
56673
|
"get_recently_updated_tokens",
|
|
@@ -55618,7 +56692,6 @@ var ToolRegistry = [
|
|
|
55618
56692
|
"moralis_get_token_metadata_batch",
|
|
55619
56693
|
"moralis_get_token_holders",
|
|
55620
56694
|
"moralis_get_token_transfers",
|
|
55621
|
-
"moralis_get_token_top_traders",
|
|
55622
56695
|
"moralis_get_token_details",
|
|
55623
56696
|
"moralis_get_sol_token_price",
|
|
55624
56697
|
"moralis_get_sol_token_metadata",
|
|
@@ -55696,7 +56769,6 @@ var ToolRegistry = [
|
|
|
55696
56769
|
"alchemy_get_floor_price",
|
|
55697
56770
|
"alchemy_get_contracts_for_owner",
|
|
55698
56771
|
"alchemy_is_spam_contract",
|
|
55699
|
-
"alchemy_is_holder_of_contract",
|
|
55700
56772
|
"alchemy_solana_get_asset",
|
|
55701
56773
|
"alchemy_solana_get_asset_proof",
|
|
55702
56774
|
"alchemy_solana_get_asset_proofs",
|
|
@@ -55798,9 +56870,7 @@ var ToolRegistry = [
|
|
|
55798
56870
|
"tenderly_get_contract_abi",
|
|
55799
56871
|
"tenderly_get_storage_changes",
|
|
55800
56872
|
"tenderly_get_transactions_range",
|
|
55801
|
-
"alchemy_get_token_allowance"
|
|
55802
|
-
"alchemy_simulate_asset_changes",
|
|
55803
|
-
"alchemy_simulate_execution"
|
|
56873
|
+
"alchemy_get_token_allowance"
|
|
55804
56874
|
]
|
|
55805
56875
|
},
|
|
55806
56876
|
{
|
|
@@ -55858,6 +56928,7 @@ var ToolRegistry = [
|
|
|
55858
56928
|
"get_exchanges_list",
|
|
55859
56929
|
"get_derivatives_exchanges_list",
|
|
55860
56930
|
"get_news",
|
|
56931
|
+
"get_coin_insights",
|
|
55861
56932
|
"moralis_search_entities",
|
|
55862
56933
|
"moralis_get_entity",
|
|
55863
56934
|
"moralis_get_entities_by_category",
|
|
@@ -55973,7 +57044,7 @@ import {
|
|
|
55973
57044
|
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
55974
57045
|
import { dirname, resolve as resolvePath } from "path";
|
|
55975
57046
|
import { fileURLToPath } from "url";
|
|
55976
|
-
import
|
|
57047
|
+
import Coingecko5 from "@coingecko/coingecko-typescript";
|
|
55977
57048
|
|
|
55978
57049
|
// src/tools/dynamic-tools.ts
|
|
55979
57050
|
import { z as z4 } from "zod";
|
|
@@ -56289,20 +57360,51 @@ function isUnmarkedErrorResponse(result2) {
|
|
|
56289
57360
|
}
|
|
56290
57361
|
return false;
|
|
56291
57362
|
}
|
|
57363
|
+
var PROVIDER_SIDE_ERROR_COPY = {
|
|
57364
|
+
plan_required: {
|
|
57365
|
+
message: "The upstream provider declined this call: the endpoint is outside this deployment's provider plan or quota (runtime_status plan_required). The arguments were not the problem.",
|
|
57366
|
+
suggestion: "Use search_tools to pick another tool for the same data. Retrying unchanged will fail again."
|
|
57367
|
+
},
|
|
57368
|
+
rate_limited: {
|
|
57369
|
+
message: "The upstream provider rate limit was hit (runtime_status rate_limited).",
|
|
57370
|
+
suggestion: "Wait about 60 seconds before one retry, or narrow the request with this tool's filters."
|
|
57371
|
+
},
|
|
57372
|
+
missing_key: {
|
|
57373
|
+
suggestion: "This provider has no working credential on this deployment. Use search_tools to pick another tool for the same data; self-hosted deployments can configure the provider's API key."
|
|
57374
|
+
}
|
|
57375
|
+
};
|
|
57376
|
+
var VENDOR_ACCOUNT_URL = /https?:\/\/[^\s"')\]]*(?:billing|pricing|plan|upgrade|admin|dashboard|account|subscri|checkout)[^\s"')\]]*/gi;
|
|
57377
|
+
function sanitizeProviderError(message, suggestion) {
|
|
57378
|
+
const copy = PROVIDER_SIDE_ERROR_COPY[classifyRuntimeStatusFromError(message)];
|
|
57379
|
+
return {
|
|
57380
|
+
message: (copy?.message ?? message).replace(
|
|
57381
|
+
VENDOR_ACCOUNT_URL,
|
|
57382
|
+
"[link removed]"
|
|
57383
|
+
),
|
|
57384
|
+
suggestion: copy?.suggestion ?? suggestion,
|
|
57385
|
+
providerSide: Boolean(copy)
|
|
57386
|
+
};
|
|
57387
|
+
}
|
|
57388
|
+
function collapseErrorMarkers(text) {
|
|
57389
|
+
return text.replace(/(?:\*\*Error:\*\*\s*){2,}/g, "**Error:** ");
|
|
57390
|
+
}
|
|
56292
57391
|
function normalizeErrorText(text, toolName, args) {
|
|
56293
57392
|
let errorMsg = "";
|
|
56294
57393
|
let code = "";
|
|
56295
57394
|
let suggestion = "";
|
|
56296
|
-
let alternatives =
|
|
57395
|
+
let alternatives = [];
|
|
56297
57396
|
let documentation = "";
|
|
56298
57397
|
try {
|
|
56299
57398
|
const parsed = JSON.parse(text);
|
|
56300
57399
|
if (parsed && typeof parsed === "object" && typeof parsed.error === "string") {
|
|
56301
57400
|
errorMsg = parsed.error;
|
|
57401
|
+
if (typeof parsed.message === "string" && parsed.message.trim() && !errorMsg.includes(parsed.message)) {
|
|
57402
|
+
errorMsg += `: ${parsed.message.replace(/https?:\/\/\S+/g, "[url]")}`;
|
|
57403
|
+
}
|
|
56302
57404
|
code = typeof parsed.code === "string" ? parsed.code : "";
|
|
56303
57405
|
suggestion = parsed.suggestion || "";
|
|
56304
57406
|
if (Array.isArray(parsed.alternatives) && parsed.alternatives.length > 0) {
|
|
56305
|
-
alternatives = parsed.alternatives.
|
|
57407
|
+
alternatives = parsed.alternatives.map(String);
|
|
56306
57408
|
}
|
|
56307
57409
|
documentation = parsed.documentation || "";
|
|
56308
57410
|
}
|
|
@@ -56310,7 +57412,18 @@ function normalizeErrorText(text, toolName, args) {
|
|
|
56310
57412
|
}
|
|
56311
57413
|
if (errorMsg) {
|
|
56312
57414
|
const bareErrorMsg = errorMsg.replace(/^(?:\s*\*\*Error:\*\*\s*)+/, "");
|
|
56313
|
-
|
|
57415
|
+
if (bareErrorMsg.includes("\n\n**Code:**")) {
|
|
57416
|
+
return collapseErrorMarkers(`**Error:** ${bareErrorMsg}`);
|
|
57417
|
+
}
|
|
57418
|
+
const safe = sanitizeProviderError(bareErrorMsg, suggestion);
|
|
57419
|
+
suggestion = safe.suggestion;
|
|
57420
|
+
const prefix = toolName?.split("_")[0];
|
|
57421
|
+
if (safe.providerSide && prefix) {
|
|
57422
|
+
alternatives = alternatives.filter(
|
|
57423
|
+
(name) => !name.startsWith(`${prefix}_`)
|
|
57424
|
+
);
|
|
57425
|
+
}
|
|
57426
|
+
let normalized = `**Error:** ${safe.message}`;
|
|
56314
57427
|
normalized += `
|
|
56315
57428
|
|
|
56316
57429
|
**Code:** ${code || "API_ERROR"}`;
|
|
@@ -56326,15 +57439,29 @@ function normalizeErrorText(text, toolName, args) {
|
|
|
56326
57439
|
|
|
56327
57440
|
**Params:** ${truncated}`;
|
|
56328
57441
|
}
|
|
56329
|
-
if (alternatives
|
|
57442
|
+
if (alternatives.length > 0) {
|
|
57443
|
+
normalized += `
|
|
56330
57444
|
|
|
56331
|
-
**Alternatives:** ${alternatives}`;
|
|
57445
|
+
**Alternatives:** ${alternatives.join(", ")}`;
|
|
57446
|
+
}
|
|
56332
57447
|
if (documentation) normalized += `
|
|
56333
57448
|
|
|
56334
57449
|
**Documentation:** ${documentation}`;
|
|
56335
57450
|
return normalized;
|
|
56336
57451
|
}
|
|
56337
|
-
let result2 = text;
|
|
57452
|
+
let result2 = collapseErrorMarkers(text);
|
|
57453
|
+
const head = /^\*\*Error:\*\* (.*?)(?=\n\n\*\*|$)/s.exec(result2);
|
|
57454
|
+
if (head) {
|
|
57455
|
+
const safe = sanitizeProviderError(head[1], "");
|
|
57456
|
+
result2 = result2.replace(head[0], `**Error:** ${safe.message}`);
|
|
57457
|
+
if (safe.suggestion) {
|
|
57458
|
+
result2 = result2.replace(
|
|
57459
|
+
/\*\*Suggestion:\*\* [^\n]*/,
|
|
57460
|
+
`**Suggestion:** ${safe.suggestion}`
|
|
57461
|
+
);
|
|
57462
|
+
}
|
|
57463
|
+
}
|
|
57464
|
+
result2 = result2.replace(VENDOR_ACCOUNT_URL, "[link removed]");
|
|
56338
57465
|
if (toolName && !result2.includes("**Tool:**")) {
|
|
56339
57466
|
result2 += `
|
|
56340
57467
|
|
|
@@ -56379,8 +57506,8 @@ function normalizeToolResponse(result2, toolName, args) {
|
|
|
56379
57506
|
}
|
|
56380
57507
|
|
|
56381
57508
|
// src/utils/clientRouter.ts
|
|
56382
|
-
function getClientForTool(
|
|
56383
|
-
const provider = getProviderDescriptorForTool("",
|
|
57509
|
+
function getClientForTool(metadata104, clients) {
|
|
57510
|
+
const provider = getProviderDescriptorForTool("", metadata104);
|
|
56384
57511
|
if (provider.clientStrategy === "shared-goplus") {
|
|
56385
57512
|
return clients.goplusClient ?? void 0;
|
|
56386
57513
|
}
|
|
@@ -57019,7 +58146,7 @@ function describeErrorCode(code) {
|
|
|
57019
58146
|
// src/utils/agentErrorCopy.ts
|
|
57020
58147
|
function agentErrorCopy(status, context = {}) {
|
|
57021
58148
|
const provider = context.provider || "the provider";
|
|
57022
|
-
const
|
|
58149
|
+
const tool100 = context.tool || "this tool";
|
|
57023
58150
|
switch (status) {
|
|
57024
58151
|
case "ok":
|
|
57025
58152
|
return {
|
|
@@ -57029,17 +58156,17 @@ function agentErrorCopy(status, context = {}) {
|
|
|
57029
58156
|
case "invalid_input":
|
|
57030
58157
|
return {
|
|
57031
58158
|
cause: "The arguments failed validation or named an unsupported venue/method; the provider was not the problem.",
|
|
57032
|
-
next_action: `Fix the arguments: call get_api_endpoint_schema for '${
|
|
58159
|
+
next_action: `Fix the arguments: call get_api_endpoint_schema for '${tool100}', use supported values, then retry. Do not retry unchanged.`
|
|
57033
58160
|
};
|
|
57034
58161
|
case "missing_key":
|
|
57035
58162
|
return {
|
|
57036
|
-
cause: `${provider} is not configured on this deployment (no API credential), so '${
|
|
57037
|
-
next_action: `Do not retry '${
|
|
58163
|
+
cause: `${provider} is not configured on this deployment (no API credential), so '${tool100}' cannot return data here.`,
|
|
58164
|
+
next_action: `Do not retry '${tool100}'. Use search_tools to pick a sibling provider tool for the same data, or ask the operator to configure ${provider} credentials.`
|
|
57038
58165
|
};
|
|
57039
58166
|
case "plan_required":
|
|
57040
58167
|
return {
|
|
57041
58168
|
cause: `This ${provider} endpoint needs a paid plan, quota, or credits that this deployment does not have.`,
|
|
57042
|
-
next_action: "Use search_tools to pick an alternative provider tool for the same data
|
|
58169
|
+
next_action: "Use search_tools to pick an alternative provider tool for the same data. Retrying unchanged will fail again."
|
|
57043
58170
|
};
|
|
57044
58171
|
case "rate_limited":
|
|
57045
58172
|
return {
|
|
@@ -57053,7 +58180,7 @@ function agentErrorCopy(status, context = {}) {
|
|
|
57053
58180
|
};
|
|
57054
58181
|
case "failing":
|
|
57055
58182
|
return {
|
|
57056
|
-
cause: `${provider} returned an unexpected error for '${
|
|
58183
|
+
cause: `${provider} returned an unexpected error for '${tool100}'.`,
|
|
57057
58184
|
next_action: "Do not retry in a loop. Try the route fallback tool or a sibling provider via search_tools, and report the runtime_status to the user."
|
|
57058
58185
|
};
|
|
57059
58186
|
}
|
|
@@ -57613,17 +58740,17 @@ function fitPayloadWithinReceiptBudget(value, hive, isError = false) {
|
|
|
57613
58740
|
const payloadBudget = Math.max(256, CHARACTER_LIMIT - receiptLength + 1);
|
|
57614
58741
|
const truncated = smartTruncate(JSON.stringify(payload), payloadBudget);
|
|
57615
58742
|
const parsed = parseToolText(truncated);
|
|
57616
|
-
let
|
|
57617
|
-
hive.result_digest = digestHiveCanonicalJson(
|
|
57618
|
-
if (JSON.stringify({ ...
|
|
57619
|
-
|
|
58743
|
+
let boundedPayload2 = toHiveResultPayload(parsed);
|
|
58744
|
+
hive.result_digest = digestHiveCanonicalJson(boundedPayload2);
|
|
58745
|
+
if (JSON.stringify({ ...boundedPayload2, _hive: hive }).length > CHARACTER_LIMIT) {
|
|
58746
|
+
boundedPayload2 = {
|
|
57620
58747
|
_truncated: true,
|
|
57621
58748
|
_original_length: JSON.stringify(payload).length,
|
|
57622
58749
|
_note: "Response omitted because the provider payload plus its execution receipt exceeded the response limit. Re-call with narrower parameters or filters."
|
|
57623
58750
|
};
|
|
57624
|
-
hive.result_digest = digestHiveCanonicalJson(
|
|
58751
|
+
hive.result_digest = digestHiveCanonicalJson(boundedPayload2);
|
|
57625
58752
|
}
|
|
57626
|
-
return
|
|
58753
|
+
return boundedPayload2;
|
|
57627
58754
|
}
|
|
57628
58755
|
function createHiveExecutionMetadata(endpoint, durationMs, runtimeStatus, source, warnings = [], exec, integrity = {}, credit = {}) {
|
|
57629
58756
|
const fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -58162,6 +59289,17 @@ function executionGateFailureResult(endpoint, gate, durationMs, args, credit = {
|
|
|
58162
59289
|
};
|
|
58163
59290
|
}
|
|
58164
59291
|
async function executeEndpoint(endpoint, args, context) {
|
|
59292
|
+
const result2 = await executeEndpointUnchecked(endpoint, args, context);
|
|
59293
|
+
if (result2.mcpResult.isError && endpoint.tool.outputSchema && result2.mcpResult.structuredContent !== void 0 && validateJsonSchema(
|
|
59294
|
+
result2.mcpResult.structuredContent,
|
|
59295
|
+
endpoint.tool.outputSchema
|
|
59296
|
+
).length > 0) {
|
|
59297
|
+
const { structuredContent: _omitted, ...textOnly } = result2.mcpResult;
|
|
59298
|
+
return { ...result2, mcpResult: textOnly };
|
|
59299
|
+
}
|
|
59300
|
+
return result2;
|
|
59301
|
+
}
|
|
59302
|
+
async function executeEndpointUnchecked(endpoint, args, context) {
|
|
58165
59303
|
const start = context.startMs ?? Date.now();
|
|
58166
59304
|
const validationErrors = validateEndpointArgs(endpoint, args);
|
|
58167
59305
|
if (validationErrors.length > 0) {
|
|
@@ -58277,15 +59415,22 @@ async function executeEndpoint(endpoint, args, context) {
|
|
|
58277
59415
|
const guardLimit = getAnnotatedMaxResultSizeChars(endpoint.tool) ?? DEFAULT_RESULT_SIZE_GUARD_CHARS;
|
|
58278
59416
|
const successText = getTextContent(normalized);
|
|
58279
59417
|
if (successText && successText.length > guardLimit) {
|
|
59418
|
+
const guardParams = inputSchemaParamNames(endpoint.tool.inputSchema);
|
|
58280
59419
|
bounded = {
|
|
58281
59420
|
...normalized,
|
|
58282
59421
|
content: [
|
|
58283
|
-
{
|
|
59422
|
+
{
|
|
59423
|
+
type: "text",
|
|
59424
|
+
text: smartTruncate(successText, guardLimit, {
|
|
59425
|
+
params: guardParams,
|
|
59426
|
+
offset: args?.offset
|
|
59427
|
+
})
|
|
59428
|
+
}
|
|
58284
59429
|
]
|
|
58285
59430
|
};
|
|
58286
59431
|
execForHive = { ...exec, truncated: true };
|
|
58287
59432
|
guardWarnings.push(
|
|
58288
|
-
`Response exceeded ${guardLimit} characters and was truncated for model-context safety.
|
|
59433
|
+
`Response exceeded ${guardLimit} characters and was truncated for model-context safety. ${truncationRecallHint(guardParams)}`
|
|
58289
59434
|
);
|
|
58290
59435
|
}
|
|
58291
59436
|
}
|
|
@@ -58498,7 +59643,7 @@ var CHAIN_ID_NAMES = {};
|
|
|
58498
59643
|
for (const [name, id] of Object.entries(GOPLUS_CHAIN_IDS)) {
|
|
58499
59644
|
if (!(id in CHAIN_ID_NAMES)) CHAIN_ID_NAMES[id] = name;
|
|
58500
59645
|
}
|
|
58501
|
-
var
|
|
59646
|
+
var CHAIN_ALIASES2 = [
|
|
58502
59647
|
"chain",
|
|
58503
59648
|
"network",
|
|
58504
59649
|
"chain_id",
|
|
@@ -58535,7 +59680,7 @@ var HERO_ARG_ALIASES = {
|
|
|
58535
59680
|
"name"
|
|
58536
59681
|
],
|
|
58537
59682
|
address: CONTRACT_ADDRESS_ALIASES,
|
|
58538
|
-
chain:
|
|
59683
|
+
chain: CHAIN_ALIASES2,
|
|
58539
59684
|
vs_currency: [
|
|
58540
59685
|
"vs_currency",
|
|
58541
59686
|
"vsCurrency",
|
|
@@ -58548,7 +59693,7 @@ var HERO_ARG_ALIASES = {
|
|
|
58548
59693
|
},
|
|
58549
59694
|
check_token_safety: {
|
|
58550
59695
|
address: [...CONTRACT_ADDRESS_ALIASES, "token"],
|
|
58551
|
-
chain:
|
|
59696
|
+
chain: CHAIN_ALIASES2,
|
|
58552
59697
|
response_format: RESPONSE_FORMAT_ALIASES
|
|
58553
59698
|
},
|
|
58554
59699
|
get_wallet_portfolio: {
|
|
@@ -58561,7 +59706,8 @@ var HERO_ARG_ALIASES = {
|
|
|
58561
59706
|
"account_address",
|
|
58562
59707
|
"owner"
|
|
58563
59708
|
],
|
|
58564
|
-
chain:
|
|
59709
|
+
chain: CHAIN_ALIASES2,
|
|
59710
|
+
include_spam: ["include_spam", "includeSpam", "show_spam"],
|
|
58565
59711
|
response_format: RESPONSE_FORMAT_ALIASES
|
|
58566
59712
|
}
|
|
58567
59713
|
};
|
|
@@ -58848,9 +59994,10 @@ function heroTools(endpoints, options = {}) {
|
|
|
58848
59994
|
address,
|
|
58849
59995
|
chain: chainLabel,
|
|
58850
59996
|
risk_flags: flags,
|
|
59997
|
+
...isRecord3(entry2) ? { summary: summarizeTokenSafety(entry2) } : {},
|
|
58851
59998
|
...isRecord3(entry2) && entry2.holder_count !== void 0 ? { holder_count: entry2.holder_count } : {},
|
|
58852
59999
|
...isRecord3(entry2) && entry2.token_name !== void 0 ? { token_name: entry2.token_name } : {},
|
|
58853
|
-
note: "risk_flags are raw GoPlus datapoints; treat '1' as flagged. Use response_format=detailed for the full report."
|
|
60000
|
+
note: "risk_flags are raw GoPlus datapoints; treat '1' as flagged. summary.verdicts restates the scam signals in plain language; an empty list means none fired, not that the token is safe. Use response_format=detailed for the full report."
|
|
58854
60001
|
};
|
|
58855
60002
|
};
|
|
58856
60003
|
const checkTokenSafety = {
|
|
@@ -58862,7 +60009,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
58862
60009
|
tool: {
|
|
58863
60010
|
name: "check_token_safety",
|
|
58864
60011
|
title: "Check Token Safety",
|
|
58865
|
-
description: "Token security screen for a contract address: honeypot, taxes, ownership, and related GoPlus risk datapoints. EVM chains use chain + address; Solana addresses are detected automatically.",
|
|
60012
|
+
description: "Token security screen for a contract address: honeypot, taxes, ownership, and related GoPlus risk datapoints, plus a summary of LP lock status, top-holder concentration, and creator/owner balances with plain-language verdicts. EVM chains use chain + address; Solana addresses are detected automatically.",
|
|
58866
60013
|
inputSchema: zodToInputSchema(tokenSafetySchema),
|
|
58867
60014
|
outputSchema: heroOutputSchema("check_token_safety"),
|
|
58868
60015
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
@@ -58906,9 +60053,13 @@ function heroTools(endpoints, options = {}) {
|
|
|
58906
60053
|
chain: z2.string().trim().max(40).optional().describe(
|
|
58907
60054
|
'Chain name, for example "eth", "base", "arbitrum", or "solana". Defaults to eth for 0x addresses; Solana is auto-detected.'
|
|
58908
60055
|
),
|
|
60056
|
+
include_spam: z2.boolean().optional().describe(
|
|
60057
|
+
"Show likely spam airdrops (tokens with a URL or claim lure in the name, and on EVM chains tokens with no market price). Default false; the response says how many were hidden."
|
|
60058
|
+
),
|
|
58909
60059
|
response_format: RESPONSE_FORMAT
|
|
58910
60060
|
});
|
|
58911
60061
|
const MAX_CONCISE_TOKENS = 15;
|
|
60062
|
+
const SPAM_LURE = /(https?:\/\/|www\.|t\.me\/|\.(com|xyz|io|org|net|app|site|top|lol|info|claim|gift)\b|\bclaim|\bvisit\b|airdrop|\breward)/i;
|
|
58912
60063
|
const NATIVE_SYMBOLS = {
|
|
58913
60064
|
eth: "ETH",
|
|
58914
60065
|
ethereum: "ETH",
|
|
@@ -58948,10 +60099,10 @@ function heroTools(endpoints, options = {}) {
|
|
|
58948
60099
|
};
|
|
58949
60100
|
const usdFromPrices = (token, balance) => {
|
|
58950
60101
|
if (!Array.isArray(token.tokenPrices)) return null;
|
|
58951
|
-
const
|
|
60102
|
+
const usd2 = token.tokenPrices.find(
|
|
58952
60103
|
(price) => isRecord3(price) && price.currency === "usd"
|
|
58953
60104
|
);
|
|
58954
|
-
const rate =
|
|
60105
|
+
const rate = usd2 && isRecord3(usd2) ? Number(usd2.value) : NaN;
|
|
58955
60106
|
const quantity = balance == null ? NaN : Number(balance);
|
|
58956
60107
|
const value = rate * quantity;
|
|
58957
60108
|
if (!Number.isFinite(value)) return null;
|
|
@@ -58968,44 +60119,72 @@ function heroTools(endpoints, options = {}) {
|
|
|
58968
60119
|
}
|
|
58969
60120
|
return [];
|
|
58970
60121
|
};
|
|
58971
|
-
const trimWalletPortfolio = (address, chainLabel) => (payload) => {
|
|
60122
|
+
const trimWalletPortfolio = (address, chainLabel, includeSpam = false, priced = false) => (payload) => {
|
|
58972
60123
|
const walletLower = address.trim().toLowerCase();
|
|
58973
60124
|
const tokens = firstArray(payload, ["tokens", "items", "balances"]);
|
|
58974
60125
|
const summarized = tokens.map((token) => {
|
|
58975
60126
|
if (!isRecord3(token)) return token;
|
|
58976
|
-
const
|
|
60127
|
+
const metadata104 = isRecord3(token.tokenMetadata) ? token.tokenMetadata : isRecord3(token.token) ? token.token : void 0;
|
|
58977
60128
|
const fallbackAddress = typeof token.address === "string" && token.address.trim().toLowerCase() === walletLower ? null : token.address ?? null;
|
|
58978
60129
|
const tokenAddress = token.tokenAddress ?? token.mint ?? fallbackAddress;
|
|
58979
60130
|
const isNative = tokenAddress == null;
|
|
58980
|
-
const rawDecimals = token.decimals ??
|
|
60131
|
+
const rawDecimals = token.decimals ?? metadata104?.decimals;
|
|
58981
60132
|
const decimals = typeof rawDecimals === "number" ? rawDecimals : isNative ? 18 : void 0;
|
|
58982
60133
|
const balance = decodeBalance(
|
|
58983
60134
|
token.balance ?? token.amount ?? token.tokenBalance ?? token.uiAmount ?? null,
|
|
58984
60135
|
decimals
|
|
58985
60136
|
);
|
|
58986
60137
|
return {
|
|
58987
|
-
symbol: token.symbol ??
|
|
58988
|
-
name: token.name ??
|
|
60138
|
+
symbol: token.symbol ?? metadata104?.symbol ?? token.tokenSymbol ?? (isNative ? NATIVE_SYMBOLS[chainLabel] ?? null : null),
|
|
60139
|
+
name: token.name ?? metadata104?.name ?? null,
|
|
58989
60140
|
address: tokenAddress,
|
|
58990
60141
|
balance,
|
|
58991
|
-
usd_value: token.totalValueUsd ?? token.valueUsd ?? token.usd_value ?? (typeof token.totalValueUsdCents === "number" ? token.totalValueUsdCents / 100 : usdFromPrices(token, balance))
|
|
60142
|
+
usd_value: token.totalValueUsd ?? token.valueUsd ?? token.usd_value ?? (typeof token.totalValueUsdCents === "number" ? token.totalValueUsdCents / 100 : usdFromPrices(token, balance)),
|
|
60143
|
+
...token.h24_change_pct != null ? { h24_change_pct: token.h24_change_pct } : {}
|
|
58992
60144
|
};
|
|
58993
60145
|
});
|
|
58994
|
-
const
|
|
60146
|
+
const nonZero = summarized.filter(
|
|
58995
60147
|
(token) => !isRecord3(token) || token.balance !== "0"
|
|
58996
60148
|
);
|
|
60149
|
+
const isSpam = (token) => isRecord3(token) && token.address != null && (SPAM_LURE.test(`${token.name ?? ""} ${token.symbol ?? ""}`) || priced && token.usd_value == null);
|
|
60150
|
+
const held = includeSpam ? nonZero : nonZero.filter((t) => !isSpam(t));
|
|
60151
|
+
const hidden = nonZero.length - held.length;
|
|
58997
60152
|
return {
|
|
58998
60153
|
address,
|
|
58999
60154
|
chain: chainLabel,
|
|
59000
60155
|
token_count: held.length,
|
|
60156
|
+
...hidden > 0 ? {
|
|
60157
|
+
spam_hidden: {
|
|
60158
|
+
count: hidden,
|
|
60159
|
+
reason: priced ? "no market price, or a URL/claim lure in the name" : "a URL/claim lure in the name",
|
|
60160
|
+
note: "Likely spam airdrops; pass include_spam=true to show them."
|
|
60161
|
+
}
|
|
60162
|
+
} : {},
|
|
59001
60163
|
tokens: held.slice(0, MAX_CONCISE_TOKENS),
|
|
59002
60164
|
...held.length > MAX_CONCISE_TOKENS ? {
|
|
59003
60165
|
note: `Showing the first ${MAX_CONCISE_TOKENS} of ${held.length} held tokens (zero balances omitted). Use response_format=detailed for the full provider payload.`
|
|
59004
|
-
} : summarized.length >
|
|
59005
|
-
note: `${summarized.length -
|
|
60166
|
+
} : summarized.length > nonZero.length ? {
|
|
60167
|
+
note: `${summarized.length - nonZero.length} zero-balance tokens omitted. Use response_format=detailed for the full provider payload.`
|
|
59006
60168
|
} : {}
|
|
59007
60169
|
};
|
|
59008
60170
|
};
|
|
60171
|
+
const coingeckoBalances = (payload) => {
|
|
60172
|
+
const attributes = isRecord3(payload.data) && isRecord3(payload.data.attributes) ? payload.data.attributes : {};
|
|
60173
|
+
const rows = Array.isArray(attributes.balances) ? attributes.balances : [];
|
|
60174
|
+
const toNumber = (value) => value == null || !Number.isFinite(Number(value)) ? null : Number(value);
|
|
60175
|
+
return {
|
|
60176
|
+
total_value_usd: toNumber(attributes.total_value_usd),
|
|
60177
|
+
balances: rows.filter(isRecord3).map((row) => ({
|
|
60178
|
+
symbol: row.symbol ?? null,
|
|
60179
|
+
name: row.name ?? null,
|
|
60180
|
+
tokenAddress: row.token_type === "native" ? null : row.address ?? null,
|
|
60181
|
+
balance: row.balance ?? null,
|
|
60182
|
+
decimals: 0,
|
|
60183
|
+
usd_value: toNumber(row.value_usd),
|
|
60184
|
+
h24_change_pct: toNumber(row.h24_price_change_percentage)
|
|
60185
|
+
}))
|
|
60186
|
+
};
|
|
60187
|
+
};
|
|
59009
60188
|
const getWalletPortfolio = {
|
|
59010
60189
|
metadata: {
|
|
59011
60190
|
resource: "dynamic_tools",
|
|
@@ -59015,7 +60194,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
59015
60194
|
tool: {
|
|
59016
60195
|
name: "get_wallet_portfolio",
|
|
59017
60196
|
title: "Get Wallet Portfolio",
|
|
59018
|
-
description:
|
|
60197
|
+
description: `Token holdings for a wallet with balances, USD values, and 24h price change where available. Likely spam airdrops are hidden and counted (include_spam=true shows them). EVM chains route through CoinGecko (${WALLET_BALANCE_NETWORKS.join(", ")}) with Alchemy as the fallback; Solana wallets are detected automatically and route through Helius.`,
|
|
59019
60198
|
inputSchema: zodToInputSchema(walletPortfolioSchema),
|
|
59020
60199
|
outputSchema: heroOutputSchema("get_wallet_portfolio"),
|
|
59021
60200
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
@@ -59031,20 +60210,43 @@ function heroTools(endpoints, options = {}) {
|
|
|
59031
60210
|
`Invalid arguments: ${formatSchemaIssues(parsed.error)}`
|
|
59032
60211
|
);
|
|
59033
60212
|
}
|
|
59034
|
-
const { address, chain } = parsed.data;
|
|
60213
|
+
const { address, chain, include_spam: includeSpam } = parsed.data;
|
|
59035
60214
|
const format = heroFormat(parsed.data.response_format);
|
|
59036
60215
|
if (looksLikeNameService(address)) return nameServiceError(address);
|
|
59037
60216
|
if (isSolanaChain(chain, address)) {
|
|
59038
60217
|
const result3 = await executeUnderlying("helius_get_wallet_balances", {
|
|
59039
60218
|
wallet: address
|
|
59040
60219
|
}, "get_wallet_portfolio");
|
|
59041
|
-
return respond(
|
|
60220
|
+
return respond(
|
|
60221
|
+
result3,
|
|
60222
|
+
format,
|
|
60223
|
+
trimWalletPortfolio(address, "solana", includeSpam)
|
|
60224
|
+
);
|
|
59042
60225
|
}
|
|
59043
60226
|
const normalized = normalizeChain(chain);
|
|
59044
60227
|
const network = ALCHEMY_NETWORKS[normalized];
|
|
60228
|
+
const geckoNetwork = GECKO_NETWORKS[normalized] ?? normalized;
|
|
60229
|
+
let fallbackNote;
|
|
60230
|
+
if (WALLET_BALANCE_NETWORKS.includes(geckoNetwork)) {
|
|
60231
|
+
const result3 = await executeUnderlying("get_wallet_balances", {
|
|
60232
|
+
address,
|
|
60233
|
+
networks: geckoNetwork,
|
|
60234
|
+
per_page: 100
|
|
60235
|
+
}, "get_wallet_portfolio");
|
|
60236
|
+
if (!network || "hive" in result3 && result3.ok) {
|
|
60237
|
+
return respond(result3, format, (payload) => {
|
|
60238
|
+
const balances = coingeckoBalances(payload);
|
|
60239
|
+
return {
|
|
60240
|
+
...trimWalletPortfolio(address, normalized, includeSpam, true)(balances),
|
|
60241
|
+
total_value_usd: balances.total_value_usd
|
|
60242
|
+
};
|
|
60243
|
+
});
|
|
60244
|
+
}
|
|
60245
|
+
fallbackNote = "CoinGecko wallet balances failed for this call; served from Alchemy.";
|
|
60246
|
+
}
|
|
59045
60247
|
if (!network) {
|
|
59046
60248
|
return errorResult5(
|
|
59047
|
-
`Invalid arguments: Unknown chain '${chain}'. Pass an EVM chain name (${Object.keys(ALCHEMY_NETWORKS).slice(0, 6).join(", ")}, \u2026) or "solana".`
|
|
60249
|
+
`Invalid arguments: Unknown chain '${chain}'. Pass an EVM chain name (${Object.keys(ALCHEMY_NETWORKS).slice(0, 6).join(", ")}, \u2026), a CoinGecko network id (${WALLET_BALANCE_NETWORKS.join(", ")}), or "solana".`
|
|
59048
60250
|
);
|
|
59049
60251
|
}
|
|
59050
60252
|
const result2 = await executeUnderlying("alchemy_get_tokens_by_wallet", {
|
|
@@ -59053,11 +60255,106 @@ function heroTools(endpoints, options = {}) {
|
|
|
59053
60255
|
withMetadata: true,
|
|
59054
60256
|
withPrices: true
|
|
59055
60257
|
}, "get_wallet_portfolio");
|
|
59056
|
-
return respond(result2, format,
|
|
60258
|
+
return respond(result2, format, (payload) => ({
|
|
60259
|
+
...trimWalletPortfolio(address, normalized, includeSpam, true)(payload),
|
|
60260
|
+
...fallbackNote ? { fallback_note: fallbackNote } : {}
|
|
60261
|
+
}));
|
|
59057
60262
|
}
|
|
59058
60263
|
};
|
|
59059
60264
|
return [getTokenPrice, checkTokenSafety, getWalletPortfolio];
|
|
59060
60265
|
}
|
|
60266
|
+
var FLAG_VERDICTS = {
|
|
60267
|
+
is_honeypot: "Honeypot: GoPlus says this token cannot be sold",
|
|
60268
|
+
cannot_sell_all: "Holders cannot sell their whole balance",
|
|
60269
|
+
hidden_owner: "The contract has a hidden owner",
|
|
60270
|
+
can_take_back_ownership: "Ownership can be taken back after renouncing",
|
|
60271
|
+
is_mintable: "The owner can mint more supply",
|
|
60272
|
+
transfer_pausable: "Transfers can be paused",
|
|
60273
|
+
is_blacklisted: "The contract can blacklist addresses",
|
|
60274
|
+
owner_change_balance: "The owner can change balances",
|
|
60275
|
+
selfdestruct: "The contract can self-destruct",
|
|
60276
|
+
is_airdrop_scam: "GoPlus flags this as an airdrop scam"
|
|
60277
|
+
};
|
|
60278
|
+
function percentOf(value, fraction) {
|
|
60279
|
+
const n = typeof value === "string" || typeof value === "number" ? Number(value) : NaN;
|
|
60280
|
+
if (!Number.isFinite(n)) return null;
|
|
60281
|
+
return Math.round((fraction ? n * 100 : n) * 1e3) / 1e3;
|
|
60282
|
+
}
|
|
60283
|
+
function holderView(holder) {
|
|
60284
|
+
return {
|
|
60285
|
+
address: holder.address ?? holder.account ?? null,
|
|
60286
|
+
// Solana rows use account
|
|
60287
|
+
percent: percentOf(holder.percent, true),
|
|
60288
|
+
is_contract: holder.is_contract === 1 || holder.is_contract === "1",
|
|
60289
|
+
is_locked: holder.is_locked === 1 || holder.is_locked === "1",
|
|
60290
|
+
tag: holder.tag || null
|
|
60291
|
+
};
|
|
60292
|
+
}
|
|
60293
|
+
function summarizeTokenSafety(entry2) {
|
|
60294
|
+
const verdicts = [];
|
|
60295
|
+
for (const [key, sentence] of Object.entries(FLAG_VERDICTS)) {
|
|
60296
|
+
if (entry2[key] === "1") verdicts.push(sentence);
|
|
60297
|
+
}
|
|
60298
|
+
for (const side of ["buy", "sell"]) {
|
|
60299
|
+
const tax = percentOf(entry2[`${side}_tax`], true);
|
|
60300
|
+
if (tax !== null && tax >= 10) verdicts.push(`${side === "buy" ? "Buy" : "Sell"} tax is ${tax}%`);
|
|
60301
|
+
}
|
|
60302
|
+
const lpRows = Array.isArray(entry2.lp_holders) ? entry2.lp_holders.filter(isRecord3) : [];
|
|
60303
|
+
let lp = null;
|
|
60304
|
+
if (lpRows.length > 0) {
|
|
60305
|
+
const views = lpRows.map(holderView);
|
|
60306
|
+
const top = views.reduce((a, b) => (b.percent ?? 0) > (a.percent ?? 0) ? b : a);
|
|
60307
|
+
const locked = views.reduce((sum, v) => sum + (v.is_locked ? v.percent ?? 0 : 0), 0);
|
|
60308
|
+
lp = {
|
|
60309
|
+
holder_count: entry2.lp_holder_count ?? lpRows.length,
|
|
60310
|
+
locked_percent: Math.round(locked * 1e3) / 1e3,
|
|
60311
|
+
top_holder: top
|
|
60312
|
+
};
|
|
60313
|
+
if (locked < 50 && !top.is_locked && (top.percent ?? 0) >= 50) {
|
|
60314
|
+
verdicts.push(
|
|
60315
|
+
`LP not locked: ${top.percent}% held by one ${top.is_contract ? "contract" : "EOA"}`
|
|
60316
|
+
);
|
|
60317
|
+
} else if (locked < 50) {
|
|
60318
|
+
verdicts.push(`LP mostly unlocked: only ${Math.round(locked * 1e3) / 1e3}% of LP is locked`);
|
|
60319
|
+
}
|
|
60320
|
+
}
|
|
60321
|
+
const holderRows = Array.isArray(entry2.holders) ? entry2.holders.filter(isRecord3) : [];
|
|
60322
|
+
let topHolders = null;
|
|
60323
|
+
if (holderRows.length > 0) {
|
|
60324
|
+
const views = holderRows.map(holderView);
|
|
60325
|
+
const pairs = new Set(
|
|
60326
|
+
(Array.isArray(entry2.dex) ? entry2.dex.filter(isRecord3) : []).map((d) => String(d.pair ?? "").toLowerCase())
|
|
60327
|
+
);
|
|
60328
|
+
const owners = views.filter((v) => !pairs.has(String(v.address).toLowerCase()));
|
|
60329
|
+
const top10 = owners.slice(0, 10).reduce((sum, v) => sum + (v.percent ?? 0), 0);
|
|
60330
|
+
const largest = owners[0] ?? null;
|
|
60331
|
+
topHolders = {
|
|
60332
|
+
largest,
|
|
60333
|
+
top10_percent: Math.round(top10 * 1e3) / 1e3,
|
|
60334
|
+
excludes_dex_pairs: pairs.size > 0
|
|
60335
|
+
};
|
|
60336
|
+
if (largest && (largest.percent ?? 0) >= 20) {
|
|
60337
|
+
verdicts.push(
|
|
60338
|
+
`One address holds ${largest.percent}% of supply (${largest.is_contract ? "contract" : "EOA"}, ${largest.is_locked ? "locked" : "not locked"})`
|
|
60339
|
+
);
|
|
60340
|
+
}
|
|
60341
|
+
if (top10 >= 50) verdicts.push(`Top 10 holders hold ${Math.round(top10 * 10) / 10}% of supply`);
|
|
60342
|
+
}
|
|
60343
|
+
const creator = entry2.creator_address ? {
|
|
60344
|
+
address: entry2.creator_address,
|
|
60345
|
+
balance: entry2.creator_balance ?? null,
|
|
60346
|
+
percent: percentOf(entry2.creator_percent, true)
|
|
60347
|
+
} : null;
|
|
60348
|
+
const owner = entry2.owner_address ? {
|
|
60349
|
+
address: entry2.owner_address,
|
|
60350
|
+
balance: entry2.owner_balance ?? null,
|
|
60351
|
+
percent: percentOf(entry2.owner_percent, true)
|
|
60352
|
+
} : null;
|
|
60353
|
+
for (const [label, who] of [["Creator", creator], ["Owner", owner]]) {
|
|
60354
|
+
if (who && (who.percent ?? 0) >= 5) verdicts.push(`${label} still holds ${who.percent}% of supply`);
|
|
60355
|
+
}
|
|
60356
|
+
return { verdicts, lp, top_holders: topHolders, creator, owner };
|
|
60357
|
+
}
|
|
59061
60358
|
function heroOutputSchema(name) {
|
|
59062
60359
|
return HERO_TOOL_OUTPUT_SCHEMAS[name];
|
|
59063
60360
|
}
|
|
@@ -59199,7 +60496,7 @@ var TASK_TOOLSET_ROUTES = {
|
|
|
59199
60496
|
"holders",
|
|
59200
60497
|
"Measure holder concentration with a bounded holder read.",
|
|
59201
60498
|
"get_token_top_holders",
|
|
59202
|
-
"
|
|
60499
|
+
"get_token_holder_distribution",
|
|
59203
60500
|
PROVIDER_FALLBACK
|
|
59204
60501
|
),
|
|
59205
60502
|
step(
|
|
@@ -59225,7 +60522,7 @@ var TASK_TOOLSET_ROUTES = {
|
|
|
59225
60522
|
step(
|
|
59226
60523
|
"balances",
|
|
59227
60524
|
"Read current wallet value and balances.",
|
|
59228
|
-
"
|
|
60525
|
+
"get_wallet_balances",
|
|
59229
60526
|
"alchemy_get_token_balances_by_wallet",
|
|
59230
60527
|
PROVIDER_FALLBACK
|
|
59231
60528
|
),
|
|
@@ -59264,7 +60561,7 @@ var TASK_TOOLSET_ROUTES = {
|
|
|
59264
60561
|
step(
|
|
59265
60562
|
"balances",
|
|
59266
60563
|
"Anchor activity to the current wallet state.",
|
|
59267
|
-
"
|
|
60564
|
+
"get_wallet_balances",
|
|
59268
60565
|
"alchemy_get_native_balance",
|
|
59269
60566
|
PROVIDER_FALLBACK
|
|
59270
60567
|
),
|
|
@@ -59359,15 +60656,14 @@ var TASK_TOOLSET_ROUTES = {
|
|
|
59359
60656
|
"simulation",
|
|
59360
60657
|
"Simulate execution without broadcasting.",
|
|
59361
60658
|
"tenderly_simulate_transaction",
|
|
59362
|
-
"
|
|
60659
|
+
"tenderly_node_simulate_transaction",
|
|
59363
60660
|
PROVIDER_FALLBACK
|
|
59364
60661
|
),
|
|
59365
60662
|
step(
|
|
59366
60663
|
"risk",
|
|
59367
60664
|
"Inspect simulated asset changes.",
|
|
59368
|
-
"alchemy_simulate_asset_changes",
|
|
59369
60665
|
"simulate_evm_transaction",
|
|
59370
|
-
|
|
60666
|
+
null
|
|
59371
60667
|
)
|
|
59372
60668
|
],
|
|
59373
60669
|
stopCondition: "Stop after decoding, execution outcome, and asset effects are clear; never broadcast or mutate chain state."
|
|
@@ -60191,6 +61487,7 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
60191
61487
|
optionalIdentifiers: ["symbol", "pair address", "time range"],
|
|
60192
61488
|
recommendedTools: [
|
|
60193
61489
|
"get_token_top_holders",
|
|
61490
|
+
"get_token_holder_distribution",
|
|
60194
61491
|
"get_token_pools",
|
|
60195
61492
|
"get_token_ohlcv",
|
|
60196
61493
|
"get_token_info",
|
|
@@ -60242,18 +61539,18 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
60242
61539
|
requiredIdentifiers: ["wallet address", "chain"],
|
|
60243
61540
|
optionalIdentifiers: ["token contract", "transaction hash", "time range"],
|
|
60244
61541
|
recommendedTools: [
|
|
61542
|
+
"get_wallet_balances",
|
|
61543
|
+
"get_wallet_pnl",
|
|
61544
|
+
"get_wallet_trades",
|
|
61545
|
+
"get_wallet_transfers",
|
|
60245
61546
|
"alchemy_get_tokens_by_wallet",
|
|
60246
61547
|
"alchemy_get_token_balances_by_wallet",
|
|
60247
61548
|
"alchemy_get_nfts_by_wallet",
|
|
60248
61549
|
"alchemy_get_asset_transfers",
|
|
60249
61550
|
"alchemy_get_native_balance",
|
|
60250
61551
|
"moralis_get_wallet_history",
|
|
60251
|
-
"moralis_get_wallet_token_balances",
|
|
60252
|
-
"moralis_get_wallet_net_worth",
|
|
60253
|
-
"moralis_get_wallet_profitability_summary",
|
|
60254
61552
|
"moralis_get_wallet_defi_positions",
|
|
60255
61553
|
"moralis_get_wallet_defi_summary",
|
|
60256
|
-
"moralis_get_wallet_swaps",
|
|
60257
61554
|
"moralis_get_wallet_chain_activity",
|
|
60258
61555
|
"moralis_get_wallet_insight",
|
|
60259
61556
|
"helius_get_wallet_balances",
|
|
@@ -60359,8 +61656,6 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
60359
61656
|
"alchemy_solana_get_compression_indexer_slot",
|
|
60360
61657
|
"alchemy_solana_get_latest_compression_signatures",
|
|
60361
61658
|
"alchemy_solana_get_latest_non_voting_signatures",
|
|
60362
|
-
"alchemy_simulate_asset_changes",
|
|
60363
|
-
"alchemy_simulate_execution",
|
|
60364
61659
|
"simulate_evm_transaction",
|
|
60365
61660
|
"defillama_get_hacks"
|
|
60366
61661
|
],
|
|
@@ -61590,7 +62885,11 @@ function getTaskOutputSchema(taskToolsetId) {
|
|
|
61590
62885
|
}
|
|
61591
62886
|
|
|
61592
62887
|
// src/services/execution-fallback.ts
|
|
61593
|
-
var FALLBACK_TRIGGER_STATUSES = /* @__PURE__ */ new Set([
|
|
62888
|
+
var FALLBACK_TRIGGER_STATUSES = /* @__PURE__ */ new Set([
|
|
62889
|
+
"failing",
|
|
62890
|
+
"missing_key",
|
|
62891
|
+
"plan_required"
|
|
62892
|
+
]);
|
|
61594
62893
|
function findRouteFallback(primaryToolName) {
|
|
61595
62894
|
for (const toolset of TASK_TOOLSETS) {
|
|
61596
62895
|
if (toolset.fallbackTools.length === 0) continue;
|
|
@@ -61633,9 +62932,7 @@ async function executeEndpointWithRouteFallback(endpoints, endpoint, args, conte
|
|
|
61633
62932
|
const fallbackProviderStatus = getProviderRuntimeStatus(
|
|
61634
62933
|
getProviderDescriptorForEndpoint(fallback)
|
|
61635
62934
|
);
|
|
61636
|
-
if (fallbackProviderStatus
|
|
61637
|
-
return primary;
|
|
61638
|
-
}
|
|
62935
|
+
if (FALLBACK_TRIGGER_STATUSES.has(fallbackProviderStatus)) return primary;
|
|
61639
62936
|
if (!argsMapCleanly(fallback, args)) return primary;
|
|
61640
62937
|
const fallbackResult = await executeEndpoint(fallback, args, {
|
|
61641
62938
|
...context,
|
|
@@ -63033,7 +64330,7 @@ async function runMcpProtocolHealth(options = {}) {
|
|
|
63033
64330
|
checks.push(
|
|
63034
64331
|
await timedCheck("tools_list_root_surface", async () => {
|
|
63035
64332
|
const listed = await client2.listTools();
|
|
63036
|
-
const names = listed.tools.map((
|
|
64333
|
+
const names = listed.tools.map((tool100) => tool100.name);
|
|
63037
64334
|
const expectedCount = getRootVisibleToolCount();
|
|
63038
64335
|
const expectedTools = [...ROOT_VISIBLE_TOOL_NAMES];
|
|
63039
64336
|
if (listed.tools.length !== expectedCount) {
|
|
@@ -64421,7 +65718,7 @@ async function runOAuthLifecycleHealth(overrides = {}) {
|
|
|
64421
65718
|
}
|
|
64422
65719
|
|
|
64423
65720
|
// src/health/selfTest.ts
|
|
64424
|
-
import
|
|
65721
|
+
import Coingecko4 from "@coingecko/coingecko-typescript";
|
|
64425
65722
|
|
|
64426
65723
|
// src/tools/goplus/goplusClient.ts
|
|
64427
65724
|
import { createHash as createHash8 } from "crypto";
|
|
@@ -65766,6 +67063,87 @@ var CCXTClient = class {
|
|
|
65766
67063
|
}
|
|
65767
67064
|
};
|
|
65768
67065
|
|
|
67066
|
+
// src/health/answerProbes.ts
|
|
67067
|
+
var ETH_WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
|
|
67068
|
+
var ETH_PEPE = "0x6982508145454Ce325dDbE47a25d4ec3d2311933";
|
|
67069
|
+
function isRecord4(value) {
|
|
67070
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
67071
|
+
}
|
|
67072
|
+
function largestArray(value, depth = 0) {
|
|
67073
|
+
if (Array.isArray(value)) return value;
|
|
67074
|
+
if (!isRecord4(value) || depth > 4) return [];
|
|
67075
|
+
let best = [];
|
|
67076
|
+
for (const child of Object.values(value)) {
|
|
67077
|
+
const candidate = largestArray(child, depth + 1);
|
|
67078
|
+
if (candidate.length > best.length) best = candidate;
|
|
67079
|
+
}
|
|
67080
|
+
return best;
|
|
67081
|
+
}
|
|
67082
|
+
function rowsOf(payload) {
|
|
67083
|
+
return isRecord4(payload) && Array.isArray(payload.rows) ? payload.rows : largestArray(payload);
|
|
67084
|
+
}
|
|
67085
|
+
function returnedCountMismatch(payload, rows) {
|
|
67086
|
+
const returned = isRecord4(payload) ? payload.returnedCount : void 0;
|
|
67087
|
+
return typeof returned === "number" && returned !== rows.length ? `returnedCount ${returned} != rows.length ${rows.length}` : null;
|
|
67088
|
+
}
|
|
67089
|
+
var ANSWER_PROBES = {
|
|
67090
|
+
polymarket_search_markets: {
|
|
67091
|
+
// The "JD Vance wins 2028" market resolves 2028-11-07; the event-level
|
|
67092
|
+
// fixture in selfTest's TOOL_PROBE_ARGS points at the same market.
|
|
67093
|
+
args: { query: "vance 2028", limit: 3 },
|
|
67094
|
+
check: (payload) => {
|
|
67095
|
+
const rows = rowsOf(payload);
|
|
67096
|
+
if (rows.length === 0)
|
|
67097
|
+
return "text search for 'vance 2028' found no markets";
|
|
67098
|
+
const unmatched = rows.filter((row) => {
|
|
67099
|
+
const text = JSON.stringify(row).toLowerCase();
|
|
67100
|
+
return !text.includes("vance") || !text.includes("2028");
|
|
67101
|
+
});
|
|
67102
|
+
if (unmatched.length > 0) {
|
|
67103
|
+
return `${unmatched.length} of ${rows.length} rows do not match every query term`;
|
|
67104
|
+
}
|
|
67105
|
+
return returnedCountMismatch(payload, rows);
|
|
67106
|
+
}
|
|
67107
|
+
},
|
|
67108
|
+
get_token_top_holders: {
|
|
67109
|
+
args: { network: "eth", address: ETH_WETH, limit: 20 },
|
|
67110
|
+
check: (payload) => {
|
|
67111
|
+
const holders = largestArray(payload);
|
|
67112
|
+
return holders.length > 10 ? null : `asked for 20 WETH holders, got ${holders.length}`;
|
|
67113
|
+
}
|
|
67114
|
+
},
|
|
67115
|
+
rwa_get_hyperliquid_dex_perps: {
|
|
67116
|
+
args: { limit: 100 },
|
|
67117
|
+
check: (payload) => {
|
|
67118
|
+
const rows = rowsOf(payload);
|
|
67119
|
+
if (rows.length === 0) return "no HIP-3 builder-dex rows";
|
|
67120
|
+
return returnedCountMismatch(payload, rows);
|
|
67121
|
+
}
|
|
67122
|
+
},
|
|
67123
|
+
// check_token_safety builds its LP verdict on this GoPlus read.
|
|
67124
|
+
get_token_security: {
|
|
67125
|
+
args: { chainId: "1", contract_addresses: ETH_PEPE },
|
|
67126
|
+
check: (payload) => {
|
|
67127
|
+
const result2 = isRecord4(payload) ? payload.result : void 0;
|
|
67128
|
+
const token = isRecord4(result2) ? Object.values(result2).find(isRecord4) : void 0;
|
|
67129
|
+
if (!token) return "no token entry in the GoPlus result";
|
|
67130
|
+
const lpHolders = token.lp_holders;
|
|
67131
|
+
return Array.isArray(lpHolders) && lpHolders.length > 0 ? null : "PEPE came back without lp_holders";
|
|
67132
|
+
}
|
|
67133
|
+
}
|
|
67134
|
+
};
|
|
67135
|
+
function checkAnswer(probe, result2) {
|
|
67136
|
+
const text = isRecord4(result2) ? result2.content?.[0]?.text : void 0;
|
|
67137
|
+
if (typeof text !== "string") return "response has no text content";
|
|
67138
|
+
let payload;
|
|
67139
|
+
try {
|
|
67140
|
+
payload = JSON.parse(text);
|
|
67141
|
+
} catch {
|
|
67142
|
+
return "response is not JSON";
|
|
67143
|
+
}
|
|
67144
|
+
return probe.check(payload);
|
|
67145
|
+
}
|
|
67146
|
+
|
|
65769
67147
|
// src/health/selfTest.ts
|
|
65770
67148
|
function positiveTimeout(name, fallbackMs) {
|
|
65771
67149
|
const configured = Number(process.env[name]);
|
|
@@ -65779,7 +67157,7 @@ var QUICK_PROBE_TIMEOUT = Math.min(
|
|
|
65779
67157
|
var ETH_WALLET = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
|
|
65780
67158
|
var MORALIS_EVM_WALLET = "0xF977814e90dA44bFA03b6295A0616a897441aceC";
|
|
65781
67159
|
var ETH_USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
|
|
65782
|
-
var
|
|
67160
|
+
var ETH_WETH2 = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
|
|
65783
67161
|
var ETH_USDT = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
|
|
65784
67162
|
var ETH_BAYC = "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D";
|
|
65785
67163
|
var ETH_USDC_WETH_PAIR = "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc";
|
|
@@ -65836,6 +67214,7 @@ var PROVIDER_RESOURCE_PATTERNS = [
|
|
|
65836
67214
|
["polymarket", /^polymarket(?:[.-]|$)/],
|
|
65837
67215
|
["alternative", /^signals-fear-greed$/],
|
|
65838
67216
|
["blockchain", /^signals-onchain/],
|
|
67217
|
+
["blockscout", /^blockscout(?:[.-]|$)/],
|
|
65839
67218
|
["fetch", /^fetch(?:[.-]|$)/],
|
|
65840
67219
|
["helius", /^helius\./],
|
|
65841
67220
|
["moralis", /^moralis\./],
|
|
@@ -65906,6 +67285,11 @@ var QUICK_PROBE_ARGS = {
|
|
|
65906
67285
|
toolName: "signals_get_onchain_metrics",
|
|
65907
67286
|
args: { metrics: ["hash_rate"], days: 1 }
|
|
65908
67287
|
},
|
|
67288
|
+
// One token read plus one holder page on Ethereum (keyless public host).
|
|
67289
|
+
blockscout: {
|
|
67290
|
+
toolName: "get_token_holder_distribution",
|
|
67291
|
+
args: { network: "eth", address: ETH_USDC, max_pages: 1 }
|
|
67292
|
+
},
|
|
65909
67293
|
fetch: {
|
|
65910
67294
|
toolName: "fetch_public_api",
|
|
65911
67295
|
args: { host: "api.coingecko.com", path: "/api/v3/ping" }
|
|
@@ -65944,7 +67328,7 @@ var FULL_MODE_DEFAULT_ARGS = {
|
|
|
65944
67328
|
vs_currency: "usd",
|
|
65945
67329
|
vs_currencies: "usd",
|
|
65946
67330
|
network: "eth",
|
|
65947
|
-
address:
|
|
67331
|
+
address: ETH_WETH2,
|
|
65948
67332
|
days: "7"
|
|
65949
67333
|
},
|
|
65950
67334
|
defillama: {
|
|
@@ -66026,7 +67410,11 @@ var TOOL_PROBE_ARGS = {
|
|
|
66026
67410
|
get_rwa_market_chart: { id: "gold", days: "7" },
|
|
66027
67411
|
get_rwa_issuer: { id: "ondo-tokenized-assets" },
|
|
66028
67412
|
// Polymarket fixtures verified live 2026-09-16; the event ends 2028-11-07.
|
|
66029
|
-
|
|
67413
|
+
get_token_holder_distribution: {
|
|
67414
|
+
network: "eth",
|
|
67415
|
+
address: ETH_USDC,
|
|
67416
|
+
max_pages: 1
|
|
67417
|
+
},
|
|
66030
67418
|
polymarket_get_events: { slug: "presidential-election-winner-2028" },
|
|
66031
67419
|
polymarket_get_market: {
|
|
66032
67420
|
slug: "will-jd-vance-win-the-2028-us-presidential-election"
|
|
@@ -66210,6 +67598,10 @@ var TOOL_PROBE_ARGS = {
|
|
|
66210
67598
|
},
|
|
66211
67599
|
get_multi_tokens: { network: "eth", addresses: ETH_USDC },
|
|
66212
67600
|
get_token_lists: { asset_platform_id: "ethereum" },
|
|
67601
|
+
get_wallet_balances: { address: ETH_WALLET, networks: "eth", per_page: 5 },
|
|
67602
|
+
get_wallet_pnl: { address: ETH_WALLET, networks: "eth", per_page: 5 },
|
|
67603
|
+
get_wallet_trades: { network: "eth", address: ETH_WALLET, per_page: 5 },
|
|
67604
|
+
get_wallet_transfers: { network: "eth", address: ETH_WALLET, per_page: 5 },
|
|
66213
67605
|
coingecko_get_onchain_token_top_traders: {
|
|
66214
67606
|
network: COINGECKO_TOP_TRADERS_NETWORK,
|
|
66215
67607
|
address: COINGECKO_TOP_TRADERS_TOKEN,
|
|
@@ -67235,7 +68627,7 @@ function getClientForProvider(provider, clients) {
|
|
|
67235
68627
|
}
|
|
67236
68628
|
}
|
|
67237
68629
|
function buildClients() {
|
|
67238
|
-
const coinGeckoClient = new
|
|
68630
|
+
const coinGeckoClient = new Coingecko4(getCoinGeckoClientOptions());
|
|
67239
68631
|
return {
|
|
67240
68632
|
coinGeckoClient,
|
|
67241
68633
|
goplusClient: new GoPlusClient(
|
|
@@ -67481,7 +68873,7 @@ async function getEffectiveProbeArgs(toolName, provider, clients, args) {
|
|
|
67481
68873
|
return { [idFixture.field]: id };
|
|
67482
68874
|
});
|
|
67483
68875
|
}
|
|
67484
|
-
const toolFixture = TOOL_PROBE_ARGS[toolName];
|
|
68876
|
+
const toolFixture = ANSWER_PROBES[toolName]?.args ?? TOOL_PROBE_ARGS[toolName];
|
|
67485
68877
|
if (toolFixture) return toolFixture;
|
|
67486
68878
|
return projectArgsToToolSchema(
|
|
67487
68879
|
FULL_MODE_DEFAULT_ARGS[provider] || {},
|
|
@@ -67573,6 +68965,16 @@ async function runEndpointProbe(endpoint, provider, clients, args, timeoutMs = P
|
|
|
67573
68965
|
);
|
|
67574
68966
|
}
|
|
67575
68967
|
}
|
|
68968
|
+
const answerProbe = args ? void 0 : ANSWER_PROBES[toolName];
|
|
68969
|
+
const answerProblem = answerProbe ? checkAnswer(answerProbe, result2) : null;
|
|
68970
|
+
if (answerProblem) {
|
|
68971
|
+
return {
|
|
68972
|
+
name: toolName,
|
|
68973
|
+
status: "failed",
|
|
68974
|
+
duration,
|
|
68975
|
+
error: `Answer check failed: ${answerProblem}`
|
|
68976
|
+
};
|
|
68977
|
+
}
|
|
67576
68978
|
return { name: toolName, status: "passed", duration };
|
|
67577
68979
|
} catch (err) {
|
|
67578
68980
|
const errorMsg = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -68323,8 +69725,8 @@ var WORKFLOW_REGRESSION_CASES = [
|
|
|
68323
69725
|
expectedTopToolset: "wallet_investigation",
|
|
68324
69726
|
expectedToolsets: ["wallet_investigation"],
|
|
68325
69727
|
expectedCategoryTool: "get_portfolio_wallet_endpoints",
|
|
68326
|
-
expectedSchemaTool: "
|
|
68327
|
-
expectedProvider: "
|
|
69728
|
+
expectedSchemaTool: "get_wallet_pnl",
|
|
69729
|
+
expectedProvider: "CoinGecko"
|
|
68328
69730
|
},
|
|
68329
69731
|
{
|
|
68330
69732
|
id: "wallet-defi-pnl",
|
|
@@ -68343,8 +69745,8 @@ var WORKFLOW_REGRESSION_CASES = [
|
|
|
68343
69745
|
expectedTopToolset: "wallet_investigation",
|
|
68344
69746
|
expectedToolsets: ["wallet_investigation"],
|
|
68345
69747
|
expectedCategoryTool: "get_portfolio_wallet_endpoints",
|
|
68346
|
-
expectedSchemaTool: "
|
|
68347
|
-
expectedProvider: "
|
|
69748
|
+
expectedSchemaTool: "get_wallet_trades",
|
|
69749
|
+
expectedProvider: "CoinGecko"
|
|
68348
69750
|
},
|
|
68349
69751
|
{
|
|
68350
69752
|
id: "malicious-address",
|
|
@@ -69627,6 +71029,11 @@ var TOOLSET_INTENT_HINTS = {
|
|
|
69627
71029
|
],
|
|
69628
71030
|
security_risk: [
|
|
69629
71031
|
"rugpull",
|
|
71032
|
+
"rug pull",
|
|
71033
|
+
"rug check",
|
|
71034
|
+
"rugcheck",
|
|
71035
|
+
"honeypot",
|
|
71036
|
+
"scam",
|
|
69630
71037
|
"safe to buy",
|
|
69631
71038
|
"phishing",
|
|
69632
71039
|
"malicious",
|
|
@@ -69986,6 +71393,9 @@ function catalogEntry(endpoint) {
|
|
|
69986
71393
|
material: getMcpCreditCost(endpoint.tool.name) === 1,
|
|
69987
71394
|
credit_cost: getMcpCreditCost(endpoint.tool.name),
|
|
69988
71395
|
summary: summarizeToolDescription(endpoint.tool.description ?? ""),
|
|
71396
|
+
// Ranking text: the summary minus its negative guidance, so "Not
|
|
71397
|
+
// recommended for: ... holder info" never counts as a holder match.
|
|
71398
|
+
rankText: summarizeToolDescription(endpoint.tool.description ?? "").split(/not recommended for:|don't use when:|common mistakes:/i)[0].toLowerCase(),
|
|
69989
71399
|
searchText: [
|
|
69990
71400
|
endpoint.tool.name,
|
|
69991
71401
|
endpoint.tool.title ?? getToolTitle(endpoint.tool.name),
|
|
@@ -70004,7 +71414,11 @@ function catalogEntry(endpoint) {
|
|
|
70004
71414
|
};
|
|
70005
71415
|
}
|
|
70006
71416
|
function publicCatalogEntry(entry2) {
|
|
70007
|
-
const {
|
|
71417
|
+
const {
|
|
71418
|
+
searchText: _searchText,
|
|
71419
|
+
rankText: _rankText,
|
|
71420
|
+
...publicEntry
|
|
71421
|
+
} = entry2;
|
|
70008
71422
|
return publicEntry;
|
|
70009
71423
|
}
|
|
70010
71424
|
function normalizeSearchToken(token) {
|
|
@@ -70044,31 +71458,36 @@ function matchesSearchTokens(haystack, query) {
|
|
|
70044
71458
|
return tokens.some((token) => textMatchesSearchToken(haystack, token));
|
|
70045
71459
|
}
|
|
70046
71460
|
function queryMatchScore(entry2, query) {
|
|
70047
|
-
if (!query) return 0;
|
|
71461
|
+
if (!query) return { score: 0, direct: true };
|
|
70048
71462
|
if (!matchesSearchTokens(entry2.searchText, query)) return null;
|
|
70049
71463
|
const tokens = parseSearchTokens(query);
|
|
70050
|
-
if (tokens.length === 0) return 0;
|
|
71464
|
+
if (tokens.length === 0) return { score: 0, direct: true };
|
|
70051
71465
|
const nameTitleText = [entry2.name, entry2.title].join(" ").toLowerCase();
|
|
71466
|
+
const summaryText = entry2.rankText;
|
|
70052
71467
|
const coreText = [
|
|
70053
71468
|
entry2.name,
|
|
70054
71469
|
entry2.title,
|
|
70055
71470
|
entry2.category ?? "",
|
|
70056
71471
|
entry2.provider,
|
|
70057
|
-
|
|
71472
|
+
summaryText
|
|
70058
71473
|
].join(" ").toLowerCase();
|
|
70059
71474
|
const nameTitleTokenSet = new Set(parseSearchTokens(nameTitleText));
|
|
70060
71475
|
const coreTextTokenSet = new Set(parseSearchTokens(coreText));
|
|
71476
|
+
const providerTokenSet = new Set(parseSearchTokens(entry2.provider));
|
|
71477
|
+
let direct = false;
|
|
70061
71478
|
const score = tokens.reduce((total, token) => {
|
|
70062
71479
|
const exactToken = singularizeToken(normalizeSearchToken(token));
|
|
70063
|
-
|
|
70064
|
-
|
|
70065
|
-
if (
|
|
70066
|
-
|
|
70067
|
-
|
|
70068
|
-
|
|
71480
|
+
const providerBonus = providerTokenSet.has(exactToken) ? 8 : 0;
|
|
71481
|
+
const directScore = nameTitleTokenSet.has(exactToken) ? 24 : coreTextTokenSet.has(exactToken) ? 14 : nameTitleText.includes(exactToken) ? 12 : summaryText.includes(exactToken) ? 6 : coreText.includes(exactToken) ? 4 : 0;
|
|
71482
|
+
if (directScore > 0) {
|
|
71483
|
+
direct = true;
|
|
71484
|
+
return total + directScore + providerBonus;
|
|
71485
|
+
}
|
|
71486
|
+
if (textMatchesSearchToken(coreText, token)) return total + 2;
|
|
71487
|
+
if (textMatchesSearchToken(entry2.searchText, token)) return total + 1;
|
|
70069
71488
|
return total;
|
|
70070
71489
|
}, 0);
|
|
70071
|
-
return score > 0 ? score : null;
|
|
71490
|
+
return score > 0 ? { score, direct } : null;
|
|
70072
71491
|
}
|
|
70073
71492
|
function buildProviderByToolName(endpoints) {
|
|
70074
71493
|
const providerByToolName = /* @__PURE__ */ new Map();
|
|
@@ -70147,6 +71566,26 @@ function selectToolsetRoutes(routes, query, routeId) {
|
|
|
70147
71566
|
const positive = ranked.filter((item) => item.score > 0);
|
|
70148
71567
|
return (positive.length > 0 ? positive : ranked).slice(0, 1).map((item) => item.route);
|
|
70149
71568
|
}
|
|
71569
|
+
function promoteHealthyRouteFallbacks(route, availabilityForTool) {
|
|
71570
|
+
let promoted = false;
|
|
71571
|
+
const steps = route.steps.map((step2) => {
|
|
71572
|
+
const primaryAvailability = availabilityForTool(step2.primaryTool);
|
|
71573
|
+
const fallbackTool = step2.fallback.tool;
|
|
71574
|
+
if (!isUnavailable(primaryAvailability) || !fallbackTool || isUnavailable(availabilityForTool(fallbackTool))) {
|
|
71575
|
+
return step2;
|
|
71576
|
+
}
|
|
71577
|
+
promoted = true;
|
|
71578
|
+
return {
|
|
71579
|
+
...step2,
|
|
71580
|
+
primaryTool: fallbackTool,
|
|
71581
|
+
fallback: {
|
|
71582
|
+
tool: step2.primaryTool,
|
|
71583
|
+
when: `Promoted fallback: ${step2.primaryTool} is ${primaryAvailability} right now and returns runtime_status, not data. Original fallback condition: ${step2.fallback.when}`
|
|
71584
|
+
}
|
|
71585
|
+
};
|
|
71586
|
+
});
|
|
71587
|
+
return promoted ? { ...route, steps } : route;
|
|
71588
|
+
}
|
|
70150
71589
|
function publicRouteEntry(route) {
|
|
70151
71590
|
return {
|
|
70152
71591
|
id: route.id,
|
|
@@ -70160,9 +71599,11 @@ function publicRouteEntry(route) {
|
|
|
70160
71599
|
stopCondition: route.stopCondition
|
|
70161
71600
|
};
|
|
70162
71601
|
}
|
|
70163
|
-
function publicToolsetEntry(entry2, detail, query, routeId) {
|
|
71602
|
+
function publicToolsetEntry(entry2, detail, query, routeId, availabilityForTool = () => "ok") {
|
|
70164
71603
|
const routes = selectToolsetRoutes(entry2.routes, query, routeId).map(
|
|
70165
|
-
publicRouteEntry
|
|
71604
|
+
(route) => publicRouteEntry(
|
|
71605
|
+
promoteHealthyRouteFallbacks(route, availabilityForTool)
|
|
71606
|
+
)
|
|
70166
71607
|
);
|
|
70167
71608
|
const compact = {
|
|
70168
71609
|
id: entry2.id,
|
|
@@ -70193,6 +71634,11 @@ function publicToolsetEntry(entry2, detail, query, routeId) {
|
|
|
70193
71634
|
}
|
|
70194
71635
|
function toolsetQueryMatchScore(entry2, query) {
|
|
70195
71636
|
if (!query) return 0;
|
|
71637
|
+
if (entry2.id === "solana_analysis" && /\b0x[0-9a-f]{40}\b|\b(ethereum|base|arbitrum|optimism|polygon|bsc|avalanche)\b/i.test(
|
|
71638
|
+
query
|
|
71639
|
+
) && !/\b(solana|sol|spl|pump\.?fun|raydium|jupiter)\b/i.test(query)) {
|
|
71640
|
+
return null;
|
|
71641
|
+
}
|
|
70196
71642
|
const haystack = [
|
|
70197
71643
|
entry2.id,
|
|
70198
71644
|
entry2.label,
|
|
@@ -70368,17 +71814,15 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
70368
71814
|
const filteredTools = catalogEntries.map((entry2, index) => ({
|
|
70369
71815
|
entry: entry2,
|
|
70370
71816
|
index,
|
|
70371
|
-
|
|
70372
|
-
})).filter((item) => item.
|
|
71817
|
+
match: routeId ? { score: 100, direct: true } : queryMatchScore(entry2, query)
|
|
71818
|
+
})).filter((item) => item.match !== null).filter(
|
|
70373
71819
|
(item) => selectedRouteToolNames ? selectedRouteToolNames.has(item.entry.name) : true
|
|
70374
71820
|
).filter(
|
|
70375
71821
|
(entry2) => provider ? entry2.entry.provider.toLowerCase() === provider : true
|
|
70376
71822
|
).filter(
|
|
70377
71823
|
(entry2) => category ? entry2.entry.category?.toLowerCase() === category : true
|
|
70378
71824
|
).sort(
|
|
70379
|
-
(a, b) =>
|
|
70380
|
-
).sort(
|
|
70381
|
-
(a, b) => Number(isUnavailable(availabilityFor(a.entry.provider))) - Number(isUnavailable(availabilityFor(b.entry.provider)))
|
|
71825
|
+
(a, b) => Number(b.match.direct) - Number(a.match.direct) || Number(isUnavailable(availabilityFor(a.entry.provider))) - Number(isUnavailable(availabilityFor(b.entry.provider))) || (query ? b.match.score - a.match.score : 0) || a.index - b.index
|
|
70382
71826
|
).map((item) => item.entry);
|
|
70383
71827
|
const filteredToolsets = taskToolsetCatalogEntries.map((entry2, index) => ({
|
|
70384
71828
|
entry: entry2,
|
|
@@ -70432,7 +71876,8 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
70432
71876
|
entry2,
|
|
70433
71877
|
parsed.detail ?? "compact",
|
|
70434
71878
|
query,
|
|
70435
|
-
routeId
|
|
71879
|
+
routeId,
|
|
71880
|
+
(toolName) => availabilityFor(providerByToolName.get(toolName) ?? "Hive")
|
|
70436
71881
|
);
|
|
70437
71882
|
const providerAvailability = Object.fromEntries(
|
|
70438
71883
|
entry2.providers.map((name) => [
|
|
@@ -71258,7 +72703,7 @@ function getRootToolSet() {
|
|
|
71258
72703
|
tools: Array.from(toolsByName.values()),
|
|
71259
72704
|
visibleTools: ROOT_VISIBLE_TOOL_ORDER.map(
|
|
71260
72705
|
(name) => compatibilityByName.get(name)
|
|
71261
|
-
).filter((
|
|
72706
|
+
).filter((tool100) => Boolean(tool100))
|
|
71262
72707
|
};
|
|
71263
72708
|
secureLogger.info("Root MCP tool catalog cached", {
|
|
71264
72709
|
actualToolCount: actualToolEndpoints.length,
|
|
@@ -71306,7 +72751,7 @@ function getSharedClients() {
|
|
|
71306
72751
|
);
|
|
71307
72752
|
}
|
|
71308
72753
|
sharedClients = {
|
|
71309
|
-
coinGeckoClient: new
|
|
72754
|
+
coinGeckoClient: new Coingecko5(getCoinGeckoClientOptions()),
|
|
71310
72755
|
goplusClient,
|
|
71311
72756
|
ccxtClient: new CCXTClient()
|
|
71312
72757
|
};
|
|
@@ -71339,6 +72784,7 @@ function describeToolReference(toolName, endpointIndex) {
|
|
|
71339
72784
|
};
|
|
71340
72785
|
}
|
|
71341
72786
|
function describeTaskToolset(toolset, endpointIndex) {
|
|
72787
|
+
const availability = getProviderAvailabilitySnapshot().byProviderName;
|
|
71342
72788
|
return {
|
|
71343
72789
|
id: toolset.id,
|
|
71344
72790
|
kind: toolset.kind,
|
|
@@ -71347,7 +72793,12 @@ function describeTaskToolset(toolset, endpointIndex) {
|
|
|
71347
72793
|
outcome: toolset.outcome,
|
|
71348
72794
|
requiredIdentifiers: toolset.requiredIdentifiers,
|
|
71349
72795
|
optionalIdentifiers: toolset.optionalIdentifiers,
|
|
71350
|
-
routes: toolset.routes
|
|
72796
|
+
routes: toolset.routes.map(
|
|
72797
|
+
(route) => promoteHealthyRouteFallbacks(route, (toolName) => {
|
|
72798
|
+
const endpoint = endpointIndex.get(toolName);
|
|
72799
|
+
return endpoint ? availability[getProviderNameForTool(endpoint)] ?? "ok" : "ok";
|
|
72800
|
+
})
|
|
72801
|
+
),
|
|
71351
72802
|
coverageCatalog: {
|
|
71352
72803
|
recommendedTools: toolset.recommendedTools.map(
|
|
71353
72804
|
(toolName) => describeToolReference(toolName, endpointIndex)
|
|
@@ -71524,21 +72975,21 @@ var HiveMCPServer = class {
|
|
|
71524
72975
|
}
|
|
71525
72976
|
registerAllTools() {
|
|
71526
72977
|
const registeredTools = this.scope.kind === "root" ? this.compatibilityToolEndpoints.map((endpoint) => endpoint.tool) : this.visibleTools;
|
|
71527
|
-
for (const
|
|
71528
|
-
const inputSchema =
|
|
72978
|
+
for (const tool100 of registeredTools) {
|
|
72979
|
+
const inputSchema = tool100.inputSchema;
|
|
71529
72980
|
const inputZodSchema = memoizedJsonSchemaToZodInputSchema(inputSchema);
|
|
71530
|
-
const outputSchema =
|
|
72981
|
+
const outputSchema = tool100.outputSchema;
|
|
71531
72982
|
const outputZodShape = outputSchema?.type === "object" ? memoizedJsonSchemaToZodOutputSchema(outputSchema) : void 0;
|
|
71532
|
-
const toolName =
|
|
71533
|
-
const toolMeta =
|
|
72983
|
+
const toolName = tool100.name;
|
|
72984
|
+
const toolMeta = tool100._meta;
|
|
71534
72985
|
this.server.registerTool(
|
|
71535
72986
|
toolName,
|
|
71536
72987
|
{
|
|
71537
|
-
title:
|
|
71538
|
-
description:
|
|
72988
|
+
title: tool100.title,
|
|
72989
|
+
description: tool100.description,
|
|
71539
72990
|
inputSchema: inputZodSchema,
|
|
71540
72991
|
...outputZodShape && { outputSchema: outputZodShape },
|
|
71541
|
-
annotations:
|
|
72992
|
+
annotations: tool100.annotations,
|
|
71542
72993
|
// Surfaces response-size headroom annotations (plan 1.15) in
|
|
71543
72994
|
// SDK-generated tools/list responses.
|
|
71544
72995
|
...toolMeta && { _meta: toolMeta }
|