@sharpe-terminal/mcp-server 1.2.0 → 1.4.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/dist/tools.js CHANGED
@@ -1,7 +1,55 @@
1
1
  import { z } from "zod";
2
2
  import { FUTURES_CHARTS } from "./resources.js";
3
+ import { MAX_ITEMS, paginateRows, paginateSnapshotPayload, rowTimeMs, } from "./tool-runtime.js";
3
4
  // -- Tool schemas (Zod shapes for McpServer.tool()) --
4
5
  const FUTURES_CHART_IDS = FUTURES_CHARTS.map((chart) => chart.id);
6
+ const pageCursorSchema = z
7
+ .string()
8
+ .optional()
9
+ .describe("Pagination cursor returned by the previous page. Only meaningful alongside limit.");
10
+ export const globalOverviewSchema = {
11
+ tab: z
12
+ .enum(["crypto", "equity", "preipo", "etf", "index", "commodity", "fx"])
13
+ .default("crypto")
14
+ .describe("Universe to return rows for. 'crypto' = crypto perps; the rest are RWA perp classes. The market-wide metric block is identical on every tab. The 'basket' asset class has no tab and is rejected."),
15
+ limit: z
16
+ .number()
17
+ .int()
18
+ .min(1)
19
+ .max(1000)
20
+ .optional()
21
+ .describe("Rows per page (1-1000). A paged response is { data, pagination } holding only the ranked rows; the market-wide metric block comes back on unpaged calls. Responses are additionally capped at 200 items, so pass limit together with cursor to page through every row."),
22
+ cursor: pageCursorSchema,
23
+ };
24
+ export const rwaPerpRatesSchema = {
25
+ type: z
26
+ .enum(["current", "history", "stats"])
27
+ .default("current")
28
+ .describe("Type of rwa-perp data. 'current' = latest funding/rollover snapshot per venue market, 'history' = session-tagged settlement time-series for one symbol (Hyperliquid + Kraken), 'stats' = best carry, top cross-venue spread, weekend premium, and tracked open interest."),
29
+ symbol: z
30
+ .string()
31
+ .optional()
32
+ .describe("Underlying equity ticker (e.g. TSLA, NVDA, MSTR). Required for type=history. GOOG (Ostium) and GOOGL (Hyperliquid/Kraken) are distinct share classes."),
33
+ venue: z
34
+ .string()
35
+ .optional()
36
+ .describe("Filter results to a single venue, e.g. Binance, Bybit, OKX, Bitget, Gate, Kraken, Coinbase, Aster, Lighter, Extended, GRVT, ApeX, Pacifica, Orderly, Ostium, Avantis, or a Hyperliquid HIP-3 builder market (Hyperliquid, Felix, Kinetiq, dreamcash, Paragon, Ventuals). Free-form because the venue list is registry-driven and grows."),
37
+ days: z
38
+ .number()
39
+ .min(1)
40
+ .max(90)
41
+ .default(7)
42
+ .optional()
43
+ .describe("Days of settlement history to retrieve (type=history only). Range: 1-90."),
44
+ limit: z
45
+ .number()
46
+ .int()
47
+ .min(1)
48
+ .max(5000)
49
+ .optional()
50
+ .describe("Rows per page for type=current and type=history (1-5000); a paged response is { data, pagination }. Responses are additionally capped at 200 items, so pass limit together with cursor to page through every row."),
51
+ cursor: pageCursorSchema,
52
+ };
5
53
  export const fundingRatesSchema = {
6
54
  type: z
7
55
  .enum(["current", "accumulated", "history"])
@@ -10,7 +58,19 @@ export const fundingRatesSchema = {
10
58
  coin: z
11
59
  .string()
12
60
  .optional()
13
- .describe("Base coin ticker (e.g. BTC, ETH, SOL). Filters results to this base coin across all exchanges. Required for type=history; optional for current/accumulated."),
61
+ .describe("Coin ticker, lot spelling or full name (e.g. BTC, PEPE, 1000PEPE, kPEPE, bitcoin). Returns every contract of that asset across all exchanges, whatever its base_coin spelling: PEPE also returns the 1000PEPE and KPEPE lot contracts (read lot_multiplier). Required for type=history; optional for current/accumulated."),
62
+ exchange: z
63
+ .string()
64
+ .optional()
65
+ .describe("Filter to a single venue. Values are title case exactly as returned in the `exchange` field (e.g. Binance, Bybit, OKX, Gate.io, Hyperliquid, Kraken, Aster, Lighter, dYdX, Crypto.com, edgeX, tradeXYZ); lowercase slugs such as 'gate-io' also resolve. Free-form because the venue list is registry-driven and grows. Read sharpe://exchanges for the current list."),
66
+ margin: z
67
+ .enum(["linear", "inverse"])
68
+ .optional()
69
+ .describe("Collateral convention: 'linear' = quote-margined (USDT/USDC), 'inverse' = coin-margined. Unfiltered by default."),
70
+ asset_class: z
71
+ .enum(["crypto", "equity", "commodity", "fx", "index"])
72
+ .optional()
73
+ .describe("Underlying category of the perpetual. Unfiltered by default, so tokenized-equity, commodity, index and FX perps are returned alongside crypto; pass 'crypto' to rank crypto only."),
14
74
  days: z
15
75
  .number()
16
76
  .min(1)
@@ -18,6 +78,42 @@ export const fundingRatesSchema = {
18
78
  .default(30)
19
79
  .optional()
20
80
  .describe("Number of days of history to retrieve (type=history only). Range: 1-1095."),
81
+ limit: z
82
+ .number()
83
+ .int()
84
+ .min(1)
85
+ .max(5000)
86
+ .optional()
87
+ .describe("Maximum rows to return. Responses are additionally capped at 200 items to protect the agent context window, so pass limit together with cursor to page through the whole book."),
88
+ cursor: z
89
+ .string()
90
+ .optional()
91
+ .describe("Pagination cursor returned by the previous page. Only meaningful alongside limit."),
92
+ };
93
+ export const fundingSettlementSchema = {
94
+ window: z
95
+ .enum(["current", "1d", "3d", "7d"])
96
+ .default("current")
97
+ .describe("Settlement window. 'current' is the next settlement, priced live off open interest and the forward-looking rate (predicted_rate when a venue publishes one, else the in-progress rate) -- always estimated, never realised yet. '1d'/'3d'/'7d' are realised windows; one whose coverage is below 90% carries partial: true in its window value because its history is still accruing, not a completed total."),
98
+ class: z
99
+ .enum(["all", "crypto", "rwa"])
100
+ .default("all")
101
+ .describe("Asset universe. 'rwa' = tokenized equity, commodity, FX and index perps. crypto + rwa always equal the all totals."),
102
+ coin: z
103
+ .string()
104
+ .optional()
105
+ .describe("Comma-separated base coin tickers (e.g. 'BTC' or 'BTC,ETH,SOL') to filter rows to. `totals` stays scoped to the selected class regardless of this filter -- read a filtered coin's own row for its number, not totals."),
106
+ exchange: z
107
+ .string()
108
+ .optional()
109
+ .describe("Keep only coins that have a contract on this venue. Each row's windows and open interest stay all-venue totals, not this venue's own figures. Free-form because the venue list is registry-driven and grows; an unresolvable value is rejected rather than silently returning an empty book."),
110
+ limit: z
111
+ .number()
112
+ .int()
113
+ .min(1)
114
+ .max(1000)
115
+ .optional()
116
+ .describe("Maximum rows to return, in market-cap rank order (unranked coins follow alphabetically). Defaults to 100."),
21
117
  };
22
118
  export const futuresDataSchema = {
23
119
  chart: z
@@ -37,6 +133,14 @@ export const futuresDataSchema = {
37
133
  .string()
38
134
  .optional()
39
135
  .describe("Comma-separated list of exchanges to include (e.g. Binance,Bybit,OKX,Deribit,Hyperliquid)."),
136
+ limit: z
137
+ .number()
138
+ .int()
139
+ .min(1)
140
+ .max(10000)
141
+ .optional()
142
+ .describe("Rows per page (1-10000), oldest first, returned with a pagination cursor. Paged rows are raw chart rows, oi-snapshot included. Responses are additionally capped at 200 items, so pass limit together with cursor to page through the full window."),
143
+ cursor: pageCursorSchema,
40
144
  };
41
145
  export const futuresCoinsSchema = {};
42
146
  export const correlationMatrixSchema = {
@@ -48,7 +152,7 @@ export const correlationMatrixSchema = {
48
152
  ids: z
49
153
  .string()
50
154
  .optional()
51
- .describe("Comma-separated list of crypto asset IDs (e.g. bitcoin,ethereum,solana). If omitted, returns the default set of top assets."),
155
+ .describe("Comma-separated asset IDs. Use CoinGecko IDs for crypto (e.g. bitcoin,ethereum) and Sharpe IDs for TradFi (e.g. sp500,gold,nvda). If omitted, returns the default mixed set."),
52
156
  };
53
157
  export const arbitrageSpotPerpSchema = {
54
158
  exchange: z
@@ -60,13 +164,27 @@ export const arbitrageSpotPerpSchema = {
60
164
  .enum(["all", "long", "short"])
61
165
  .default("all")
62
166
  .optional()
63
- .describe("Filter by trade direction."),
167
+ .describe("Filter by perpetual-leg direction: short for positive funding capture, long for negative funding capture."),
64
168
  };
65
169
  export const arbitrageCrossExchangeSchema = {
66
170
  exchanges: z
67
171
  .string()
68
172
  .optional()
69
- .describe("Comma-separated list of exchanges to compare (e.g. Binance,Bybit,OKX)."),
173
+ .describe("Comma-separated list of venues to compare (e.g. Binance,Bybit,Hyperliquid). Omit for all 32 funding venues."),
174
+ minOiUsd: z
175
+ .number()
176
+ .min(0)
177
+ .optional()
178
+ .describe("Minimum open interest in USD on the smaller leg. Legs with unknown OI fail the floor. Default 0 (no floor)."),
179
+ minVolUsd: z
180
+ .number()
181
+ .min(0)
182
+ .optional()
183
+ .describe("Minimum 24h volume in USD on the smaller leg. Legs with unknown volume fail the floor. Default 0 (no floor)."),
184
+ assetClass: z
185
+ .enum(["all", "crypto", "rwa"])
186
+ .optional()
187
+ .describe('Underlying filter: "all" (default), "crypto", or "rwa" (equity, commodity, FX and index perps). Pairs never cross asset classes.'),
70
188
  };
71
189
  export const arbitrageScannerSchema = {
72
190
  coin: z
@@ -80,17 +198,26 @@ export const arbitrageScannerSchema = {
80
198
  minApr: z
81
199
  .number()
82
200
  .optional()
83
- .describe("Minimum APR or carry edge in percentage points."),
201
+ .describe("Minimum product-specific fee-adjusted net APR in percentage points."),
84
202
  minOiUsd: z
85
203
  .number()
86
204
  .min(0)
87
205
  .optional()
88
206
  .describe("Minimum open interest in USD."),
207
+ minVolumeUsd: z
208
+ .number()
209
+ .min(0)
210
+ .optional()
211
+ .describe("Minimum 24-hour volume in USD."),
89
212
  minDepthUsd: z
90
213
  .number()
91
214
  .min(0)
92
215
  .optional()
93
216
  .describe("Minimum executable depth in USD when available."),
217
+ marginType: z
218
+ .enum(["linear", "inverse", "both"])
219
+ .optional()
220
+ .describe("Futures margin type filter."),
94
221
  notional: z
95
222
  .number()
96
223
  .positive()
@@ -112,7 +239,10 @@ export const arbitrageScannerSchema = {
112
239
  export const cexSpotTransferScannerSchema = {
113
240
  coin: arbitrageScannerSchema.coin,
114
241
  exchanges: arbitrageScannerSchema.exchanges,
115
- minApr: arbitrageScannerSchema.minApr,
242
+ minApr: z
243
+ .number()
244
+ .optional()
245
+ .describe("Minimum fee-adjusted transfer profit in percentage points (legacy parameter name)."),
116
246
  minDepthUsd: arbitrageScannerSchema.minDepthUsd,
117
247
  notional: arbitrageScannerSchema.notional,
118
248
  limit: arbitrageScannerSchema.limit,
@@ -128,21 +258,7 @@ export const heatmapSchema = {
128
258
  .string()
129
259
  .default("top-100")
130
260
  .optional()
131
- .describe("Category filter (e.g. top-100, defi, layer-1, memes)."),
132
- };
133
- export const dexScreenerDataSchema = {
134
- category: z
135
- .enum(["volume", "trending", "gainers", "losers", "new_pairs"])
136
- .default("trending")
137
- .optional()
138
- .describe("DEX Screener category."),
139
- network: z.string().optional().describe("Optional chain/network slug."),
140
- phrase: z.string().optional().describe("Optional search phrase."),
141
- limit: z.number().min(1).max(100).default(50).optional(),
142
- };
143
- export const dexScreenerSecuritySchema = {
144
- address: z.string().describe("Token contract address."),
145
- chainId: z.number().int().positive().describe("Codex network ID."),
261
+ .describe("One of 34 category slugs (e.g. top-100, defi, layer-1, memes). The legacy top-100 slug returns the Top 50 universe."),
146
262
  };
147
263
  export const tokenScannerSchema = {
148
264
  mode: z
@@ -156,18 +272,30 @@ export const tokenScannerSchema = {
156
272
  .describe("Comma-separated Dexscreener chain IDs."),
157
273
  chain: z.string().optional().describe("Single chain for top-new mode."),
158
274
  profile: z
159
- .enum(["discovery", "balanced", "strict", "custom"])
275
+ .enum(["discovery", "balanced", "strict"])
160
276
  .optional()
161
277
  .describe("Scanner profile."),
162
- limit: z.number().min(1).max(200).default(50).optional(),
278
+ days: z.number().min(1).optional().describe("Top-new lookback in days."),
279
+ limit: z.number().min(1).max(72).default(50).optional(),
163
280
  minLiquidityUsd: z.number().min(0).optional(),
164
281
  minVolumeH24: z.number().min(0).optional(),
165
282
  minTxnsH1: z.number().min(0).optional(),
283
+ minTxnsH24: z.number().min(0).optional(),
166
284
  maxAgeHours: z.number().min(0).optional(),
285
+ includeUnknownAge: z.boolean().optional(),
286
+ sortBy: z.enum(["score", "readiness", "rs", "volume", "momentum"]).optional(),
287
+ minBreakoutReadiness: z.number().min(0).optional(),
288
+ minRelativeStrength: z.number().optional(),
289
+ maxVolLiqRatio: z.number().min(0).optional(),
290
+ minPriceChangeH1: z.number().optional(),
167
291
  };
168
292
  export const rugCheckSecuritySchema = {
169
293
  address: z.string().describe("Token contract address."),
170
- chainId: z.number().int().positive().describe("Chain/network ID."),
294
+ chainId: z
295
+ .number()
296
+ .int()
297
+ .positive()
298
+ .describe("EVM chain ID (1 Ethereum, 56 BNB Smart Chain, 8453 Base, 42161 Arbitrum, 137 Polygon, 10 Optimism, 43114 Avalanche, 146 Sonic, 80094 Berachain, and 29 more), or 1399811149 for Solana and 728126428 for Tron. An unsupported chain returns 400."),
171
299
  };
172
300
  export const rugCheckTrendingSchema = {
173
301
  limit: z.number().min(1).max(100).default(50).optional(),
@@ -205,7 +333,7 @@ export const webTrafficSchema = {
205
333
  .optional()
206
334
  .describe("Entity type."),
207
335
  mode: z
208
- .enum(["rankings", "trends", "snapshots", "market"])
336
+ .enum(["rankings", "snapshots", "market"])
209
337
  .optional()
210
338
  .describe("Payload mode."),
211
339
  tf: z
@@ -218,12 +346,11 @@ export const webTrafficSchema = {
218
346
  .optional()
219
347
  .describe("Market sub-mode when mode=market."),
220
348
  };
221
- export const katanaSchema = {};
222
349
  export const narrativesSchema = {
223
350
  narrative: z
224
351
  .string()
225
352
  .optional()
226
- .describe("Narrative slug to filter by (e.g. layer-1, layer-2, defi, ai-agents, defai, depin, desci, gaming, dex, cex, lending, memes, nfts, oracles, privacy, rwa, stablecoins, restaking, liquid-staking, modular, socialfi, intent). Omit for all narratives."),
353
+ .describe("Narrative to filter by, as the id the API matches (layer-1, layer-2, defi, ai-agents, defai, depin, desci, gaming, dex, cex, lending, memes, nfts, oracles, privacy, rwa, stablecoins, restaking, liquid-staking, modular, socialfi, intent, prediction-markets, perps, bridges, account-abstraction, tap-to-earn, telegram-apps, zk, yield-farming). Page slugs such as dex-tokens or cex-tokens return no coins; use dex or cex. Omit for all narratives."),
227
354
  correlation: z
228
355
  .string()
229
356
  .optional()
@@ -234,7 +361,7 @@ export const ecosystemsSchema = {
234
361
  ecosystem: z
235
362
  .string()
236
363
  .optional()
237
- .describe("Ecosystem slug to filter by (e.g. ethereum, solana, bnb-chain, arbitrum, base, bitcoin, avalanche, optimism, cosmos, polkadot, sui, aptos, ton, tron). Omit for all ecosystems."),
364
+ .describe("Ecosystem to filter by, as the id the API matches (e.g. ethereum, solana, bnb, arbitrum, base, bitcoin, avalanche, optimism, cosmos, polkadot, sui, aptos, ton, tron); read sharpe://ecosystems for all of them. Page slugs such as bnb-chain or toncoin return no coins; use bnb or ton. Omit for all ecosystems."),
238
365
  excludeNative: z
239
366
  .string()
240
367
  .optional()
@@ -291,6 +418,13 @@ export const newsSchema = {
291
418
  .string()
292
419
  .optional()
293
420
  .describe("ISO 8601 timestamp. Only return articles published after this time."),
421
+ q: z
422
+ .string()
423
+ .trim()
424
+ .min(1)
425
+ .max(200)
426
+ .optional()
427
+ .describe("Search article titles across the stored news corpus."),
294
428
  offset: z
295
429
  .number()
296
430
  .int()
@@ -298,36 +432,19 @@ export const newsSchema = {
298
432
  .optional()
299
433
  .describe("Offset for pagination; skip this many articles before returning results."),
300
434
  };
301
- export const curatedNewsSchema = {
302
- limit: z
303
- .number()
304
- .min(1)
305
- .max(100)
306
- .default(20)
307
- .optional()
308
- .describe("Maximum number of curated stories to return (1-100)."),
309
- category: z
310
- .enum(["all", "crypto", "ai", "markets", "geopolitics"])
311
- .optional()
312
- .describe("Filter by category: all, crypto, ai, markets, geopolitics."),
313
- cursor: z
314
- .string()
315
- .optional()
316
- .describe("Pagination cursor (ISO date or 'date|id') from a previous response."),
317
- };
318
435
  export const marketOverviewSchema = {};
319
436
  export const pricePredictionSchema = {
320
437
  coin: z
321
438
  .string()
322
439
  .optional()
323
- .describe("Canonical coin slug (e.g. bitcoin, ethereum, solana)."),
440
+ .describe("Ticker or canonical coin slug (e.g. BTC, bitcoin, solana). Omit for all coins."),
324
441
  };
325
442
  export const searchMarketCapSchema = {
326
443
  q: z
327
444
  .string()
328
445
  .min(2)
329
446
  .max(100)
330
- .describe("Search query (2-100 characters). Can be a coin name or ticker."),
447
+ .describe("Crypto or supported TradFi asset name/symbol (2-100 characters)."),
331
448
  };
332
449
  export const gemFinderSchema = {
333
450
  limit: z
@@ -337,6 +454,10 @@ export const gemFinderSchema = {
337
454
  .default(100)
338
455
  .optional()
339
456
  .describe("Maximum number of tokens to return (1-1000)."),
457
+ cursor: z
458
+ .string()
459
+ .optional()
460
+ .describe("Snapshot-bound pagination cursor returned by the previous Gem Finder page."),
340
461
  };
341
462
  export const derivativesOverviewSchema = {};
342
463
  export const apiCoverageSchema = {};
@@ -375,15 +496,25 @@ const listingExchangeIds = [
375
496
  "dydx",
376
497
  "aevo",
377
498
  ];
499
+ // /listings/data validates only the legacy compatibility venues
500
+ // (legacyListingExchangeIds in src/lib/api/schemas.ts) and 400s the rest;
501
+ // the recent/events feeds accept every id in listingExchangeIds.
502
+ const legacyListingExchangeIds = [
503
+ "binance",
504
+ "okx",
505
+ "bybit",
506
+ "gateio",
507
+ "mexc",
508
+ ];
378
509
  export const listingsDataSchema = {
379
510
  narrative: z
380
511
  .string()
381
512
  .optional()
382
513
  .describe("Filter to a single narrative slug (e.g. 'ai-agents', 'memes', 'layer-1', 'rwa', 'defi'). Uses the Sharpe narrative taxonomy."),
383
514
  exchange: z
384
- .enum(listingExchangeIds)
515
+ .enum(legacyListingExchangeIds)
385
516
  .optional()
386
- .describe("Filter to a single exchange."),
517
+ .describe("Filter to a single exchange. The aggregate hub covers the compatibility venues only; use get_recent_listings or get_listing_events for the other venues."),
387
518
  };
388
519
  export const listingsRecentSchema = {
389
520
  narrative: z
@@ -573,16 +704,158 @@ export const pumpDumpSchema = {
573
704
  .optional()
574
705
  .describe("Filter to one manipulation lifecycle phase."),
575
706
  };
707
+ // -- Keyless-path post-processing helpers --
708
+ function isRecord(value) {
709
+ return value !== null && typeof value === "object" && !Array.isArray(value);
710
+ }
711
+ function cursorArg(args) {
712
+ return typeof args.cursor === "string" && args.cursor
713
+ ? args.cursor
714
+ : undefined;
715
+ }
716
+ /**
717
+ * The stored `manipulation_phase` values a public phase param selects.
718
+ * Mirrors resolvePhaseFilter in src/app/api/v1/pump-dump/data/route.ts.
719
+ */
720
+ function resolvePhaseFilter(phase) {
721
+ if (!phase)
722
+ return null;
723
+ if (phase === "dumping")
724
+ return ["distribution", "dump-risk"];
725
+ if (phase === "dump")
726
+ return ["dump-risk"];
727
+ return [phase];
728
+ }
729
+ /**
730
+ * The free pump-dump and insider-selling routes are statically cached and
731
+ * read no query params, so limit, min_score and phase only ever reached v1
732
+ * (2026-09-24 audit). Re-apply them to the keyless `{ scores, meta }` payload
733
+ * the way the v1 routes do: score floor, phase mapping, then limit over the
734
+ * score-ordered rows. The v1 payload (`{ coins, ... }`) is already filtered.
735
+ */
736
+ export function filterSignalScores(data, args) {
737
+ if (!isRecord(data) || !Array.isArray(data.scores))
738
+ return data;
739
+ const minScore = typeof args.min_score === "number" ? args.min_score : 0;
740
+ const limit = typeof args.limit === "number" ? args.limit : 100;
741
+ const phases = resolvePhaseFilter(typeof args.phase === "string" ? args.phase : undefined);
742
+ const scores = data.scores
743
+ .filter((row) => {
744
+ if (!isRecord(row) || typeof row.score !== "number")
745
+ return false;
746
+ if (row.score < minScore)
747
+ return false;
748
+ return phases === null || phases.includes(String(row.manipulation_phase));
749
+ })
750
+ .slice(0, limit);
751
+ return { ...data, scores };
752
+ }
753
+ /**
754
+ * oi-snapshot is documented as current open interest by exchange, but both
755
+ * routes return the whole ascending window (up to 5,000 raw rows). Keep each
756
+ * exchange's newest reading, largest first, so "current" means current.
757
+ */
758
+ export function latestRowPerExchange(rows) {
759
+ const latest = new Map();
760
+ for (const row of rows) {
761
+ const at = rowTimeMs(row);
762
+ if (!isRecord(row) || at == null)
763
+ continue;
764
+ const exchange = String(row.exchange ?? "");
765
+ const prev = latest.get(exchange);
766
+ if (!prev || at >= prev.at)
767
+ latest.set(exchange, { row, at });
768
+ }
769
+ const oiOf = (row) => {
770
+ const value = Number(row.open_interest_value);
771
+ return Number.isFinite(value) ? value : Number.NEGATIVE_INFINITY;
772
+ };
773
+ return [...latest.values()]
774
+ .map((entry) => entry.row)
775
+ .sort((a, b) => oiOf(b) - oiOf(a));
776
+ }
777
+ /**
778
+ * The rows of a funding response that belong to `coin`. The funding API
779
+ * resolves `coin` to an asset and returns every contract of it, so `PEPE`
780
+ * also returns the 1000PEPE and KPEPE lot contracts, whose base_coin differs.
781
+ * A row is kept when its base_coin, asset_symbol or asset_id names the coin,
782
+ * or when it is a contract of the same asset as a row that does
783
+ * (`coin=1000PEPE` keeps the PEPE-spelled contracts too). Rows from an API
784
+ * without the asset fields, or with them null (no instrument master to read),
785
+ * match on base_coin exactly as before.
786
+ */
787
+ export function rowsForFundingCoin(rows, coin) {
788
+ const needle = coin.trim().toUpperCase();
789
+ const text = (row, key) => {
790
+ const value = row !== null && typeof row === "object"
791
+ ? row[key]
792
+ : undefined;
793
+ return typeof value === "string" ? value : null;
794
+ };
795
+ const names = (row) => [
796
+ text(row, "base_coin") ?? text(row, "coin"),
797
+ text(row, "asset_symbol"),
798
+ text(row, "asset_id"),
799
+ ].some((value) => value?.toUpperCase() === needle);
800
+ const assets = new Set(rows
801
+ .filter(names)
802
+ .map((row) => text(row, "asset_id"))
803
+ .filter((assetId) => assetId !== null));
804
+ return rows.filter((row) => {
805
+ const assetId = text(row, "asset_id");
806
+ return names(row) || (assetId !== null && assets.has(assetId));
807
+ });
808
+ }
576
809
  export const TOOL_DEFS = [
810
+ {
811
+ name: "get_global_overview",
812
+ description: "Get the whole-market derivatives board in one call: total open interest with its crypto-versus-RWA split, 24h liquidations, average Wilder RSI(14) across the top 100 crypto perps by market cap, the Altcoin Season Index (share of the top 50 by market cap beating BTC over 90 days, excluding stablecoins and wrapped/staked derivatives), open interest broken down by asset class, and a ranked per-asset row table. The tab parameter selects the universe: crypto, equity, preipo, etf, index, commodity or fx. Rows carry price, 24h price change, funding rate (interval-true, never assumed 8h), 24h volume, market cap, open interest with 1h and 24h changes, plus liquidations on crypto or a venue count on RWA tabs. Ranks are by open interest descending. Liquidations are crypto-only because no RWA venue publishes a liquidation feed. Use this for 'what is the market doing' questions; use get_funding_rates for per-venue crypto funding detail and get_rwa_perp_funding for RWA carry and cross-venue spreads.",
813
+ schema: globalOverviewSchema,
814
+ apiPath: "/api/v1/global/overview",
815
+ fallbackPath: "/api/global/overview",
816
+ // The free route ignores limit/cursor and always returns every row with
817
+ // the metric block. Page its rows into the v1 paged shape ({ data,
818
+ // pagination }, rows only) so a keyless caller can reach all of them.
819
+ postProcess: (data, args, context) => {
820
+ if (context?.paginated || typeof args.limit !== "number")
821
+ return data;
822
+ if (!isRecord(data) || !Array.isArray(data.rows))
823
+ return data;
824
+ return paginateRows(data.rows, args.limit, cursorArg(args));
825
+ },
826
+ },
827
+ {
828
+ name: "get_rwa_perp_funding",
829
+ description: "Get funding and carry for real-world-asset perpetuals (RWA perps: stocks, pre-IPO, ETFs, indices, commodities, FX) across 19 venues: CEXs (Binance, Bybit, OKX, Bitget, Gate, Kraken xStocks, Coinbase INTX), perp DEXs (Aster, Lighter, Extended, GRVT, ApeX, Pacifica, Orderly), Hyperliquid HIP-3 builder markets, and the rollover venues Ostium and Avantis. Rates are interval-true and mechanism-true: funding_apr annualizes each venue's own settlement interval (never an assumed 8h), and a both-sides rollover is reported separately as borrow_apr_annual (a cost, never mixed into funding spreads). Marks more than 2% off the cross-venue median are flagged is_price_suspect and excluded from stats and spreads. type=history returns session-tagged settlements (market/after_hours/weekend/holiday); type=stats returns best carry, top cross-venue spread, weekend premium, and open interest.",
830
+ schema: rwaPerpRatesSchema,
831
+ apiPath: "/api/v1/rwa-perps/rates",
832
+ fallbackPath: "/api/rwa-perps/rates",
833
+ // The free route returns current/history rows as a bare array and
834
+ // ignores limit/cursor; page it the way v1 does. type=stats is an object
835
+ // and passes through.
836
+ postProcess: (data, args, context) => {
837
+ if (context?.paginated || typeof args.limit !== "number")
838
+ return data;
839
+ if (!Array.isArray(data))
840
+ return data;
841
+ return paginateRows(data, args.limit, cursorArg(args));
842
+ },
843
+ },
577
844
  {
578
845
  name: "get_funding_rates",
579
- description: "Get perpetual funding rates across all supported exchanges. Returns current snapshots, accumulated rates over multiple windows, or per-coin historical rates. The coin parameter filters to a single base coin in any mode (required for history).",
846
+ description: "Get perpetual funding rates across every supported venue (see sharpe://exchanges for the current list). Returns current snapshots, accumulated rates over multiple windows, or per-coin historical rates. Units: `rate` is a decimal fraction settled over that row's own `interval_hours` (1h, 2h, 4h, 8h or 24h depending on venue and market). It is not a percent and never implicitly 8h, so annualize with rate * (8760 / interval_hours) before comparing or ranking venues. Accumulated rows carry realized sums (acc_1d/acc_7d/acc_30d/acc_90d/acc_1y) instead of a per-interval rate. Filter with coin, exchange, margin (linear/inverse) and asset_class; asset_class is unfiltered by default, so tokenized-equity, commodity, index and FX perps are included alongside crypto. `coin` names an asset: PEPE returns every PEPE contract, the 1000PEPE and KPEPE lot contracts included. Current and accumulated rows carry asset_id, asset_symbol, lot_multiplier (asset units per contract unit, e.g. 1000 on 1000PEPEUSDT), instrument_status (the venue's own status) and is_live (null = unknown). Page the full book with limit and cursor.",
580
847
  schema: fundingRatesSchema,
581
848
  apiPath: "/api/v1/funding/rates",
582
849
  fallbackPath: "/api/funding/rates",
583
- // The free /api/funding/rates endpoint ignores the `coin` param for
584
- // current/accumulated modes. Filter client-side so the target rows
585
- // survive MAX_ITEMS truncation.
850
+ // The unpaged book is ~13,000 rows (6MB) of which MAX_ITEMS survive
851
+ // truncation: page it server-side instead, so the rows come with a cursor
852
+ // (0.3s instead of 1.8s, 2026-09-26). A coin filter or history is small.
853
+ prepareArgs: (args) => args.limit == null && args.coin == null && args.type !== "history"
854
+ ? { ...args, limit: MAX_ITEMS }
855
+ : args,
856
+ // Both funding endpoints narrow by `coin` server-side. This client-side
857
+ // pass is a belt-and-braces guard so a coin-scoped request can never be
858
+ // reduced to an arbitrary alphabetical prefix by MAX_ITEMS truncation.
586
859
  postProcess: (data, args) => {
587
860
  const coin = args.coin;
588
861
  const type = args.type;
@@ -592,43 +865,85 @@ export const TOOL_DEFS = [
592
865
  if (!Array.isArray(data)) {
593
866
  return data;
594
867
  }
595
- const needle = coin.toUpperCase();
596
- return data.filter((row) => typeof row === "object" &&
597
- row !== null &&
598
- String(row.base_coin ?? "").toUpperCase() === needle);
868
+ return rowsForFundingCoin(data, coin);
869
+ },
870
+ },
871
+ {
872
+ name: "get_funding_settlement",
873
+ description: "Get the dollar value of funding actually paid at each settlement -- not just the rate -- and which side, longs or shorts, paid it. Every response carries both the per-coin row breakdown and a totals block (net/long_paid/short_paid) for the requested window and class, so 'who paid funding today' is read directly from totals rather than summed from a page of rows; totals always reflects the full requested class regardless of any coin/exchange filter on the rows. window=current (the default) is the next settlement, priced live off open interest and the forward-looking rate -- always estimated, never realised. 1d/3d/7d are realised windows; one whose coverage is below 90% carries partial: true because its history is still accruing, not a completed total. Sign convention: a window's net = long_paid - short_paid; positive means longs paid more than they received (a cost to longs), negative means shorts paid more. Both long_paid and short_paid are always non-negative magnitudes. Missing is null, never 0. class splits crypto perps from RWA perps (tokenized equity, commodity, FX, index); crypto + rwa totals always equal the all totals.",
874
+ schema: fundingSettlementSchema,
875
+ apiPath: "/api/v1/funding/settlement",
876
+ fallbackPath: "/api/funding/settlement",
877
+ // The v1 payload nests rows/totals/settlementMeta inside the envelope's
878
+ // `data` field; the free fallback puts the row array directly under
879
+ // `data` with `totals` riding alongside it (api-client.ts preserves that
880
+ // sibling instead of discarding it). Normalize both shapes into one
881
+ // `{ rows, totals, meta }` result so callers never branch on which tier
882
+ // answered, and `totals` is always the API's own aggregate -- never
883
+ // recomputed from a possibly-truncated page of rows.
884
+ postProcess: (data) => {
885
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
886
+ return data;
887
+ }
888
+ const obj = data;
889
+ const result = {
890
+ rows: obj.rows ?? obj.data,
891
+ totals: obj.totals,
892
+ };
893
+ const meta = obj.settlementMeta ?? obj.meta;
894
+ if (meta !== undefined)
895
+ result.meta = meta;
896
+ return result;
599
897
  },
600
898
  },
601
899
  {
602
900
  name: "get_futures_data",
603
- description: "Get futures chart data including open interest, volume, funding rate history, liquidations, long/short ratios, basis, term structure, and OHLCV.",
901
+ description: "Get futures chart data including open interest, volume, funding rate history, liquidations, long/short ratios, basis, term structure, and OHLCV. Time series are oldest-first; when one exceeds the response cap its newest rows are kept (truncated_fields marks kept: latest). Without limit, oi-snapshot returns each exchange's latest reading; pass limit/cursor to page raw rows oldest-first.",
604
902
  schema: futuresDataSchema,
605
903
  apiPath: "/api/v1/futures/data",
606
904
  fallbackPath: "/api/futures/data",
905
+ postProcess: (data, args, context) => {
906
+ if (context?.paginated || !isRecord(data) || !Array.isArray(data.data)) {
907
+ return data;
908
+ }
909
+ if (typeof args.limit === "number") {
910
+ // The free route ignores limit/cursor: page the same oldest-first
911
+ // rows v1 pages.
912
+ return {
913
+ ...data,
914
+ ...paginateRows(data.data, args.limit, cursorArg(args)),
915
+ };
916
+ }
917
+ if (args.chart === "oi-snapshot") {
918
+ return { ...data, data: latestRowPerExchange(data.data) };
919
+ }
920
+ return data;
921
+ },
607
922
  },
608
923
  {
609
924
  name: "get_futures_coins",
610
- description: "Get the list of coins with futures data available, including capability flags for open interest, liquidations, and long/short ratios per exchange.",
925
+ description: "Get up to 1,000 coins with futures data, including funding, open-interest, liquidation, long/short, CVD, and dated-basis capability flags plus observed venue coverage and market-cap rank.",
611
926
  schema: futuresCoinsSchema,
612
927
  apiPath: "/api/v1/futures/coins",
613
928
  fallbackPath: "/api/futures/coins",
614
929
  },
615
930
  {
616
931
  name: "get_correlation_matrix",
617
- description: "Get a price correlation matrix for crypto assets over a specified period. Useful for portfolio analysis and diversification.",
932
+ description: "Get a price correlation matrix for crypto and supported TradFi assets over a specified calendar period. Useful for portfolio analysis and diversification.",
618
933
  schema: correlationMatrixSchema,
619
934
  apiPath: "/api/v1/correlation/matrix",
620
935
  fallbackPath: "/api/correlation/matrix",
621
936
  },
622
937
  {
623
938
  name: "get_arbitrage_spot_perp",
624
- description: "Get current spot-perpetual funding rate arbitrage opportunities across exchanges. Shows the spread between spot and perp prices that can be captured via basis trading.",
939
+ description: "Get structurally executable spot-perpetual funding-capture rows with live funding, verified spot or margin availability, diagnostic basis, gross APR, and fee-adjusted net APR.",
625
940
  schema: arbitrageSpotPerpSchema,
626
941
  apiPath: "/api/v1/arbitrage/spot-perp",
627
942
  fallbackPath: "/api/arbitrage/spot-perp",
628
943
  },
629
944
  {
630
945
  name: "get_arbitrage_cross_exchange",
631
- description: "Get cross-exchange price discrepancies and arbitrage opportunities. Shows where the same asset trades at different prices across exchanges.",
946
+ description: "Get cross-exchange funding arbitrage across every funding venue and asset class (crypto plus RWA perps: equity, commodity, FX, index — pairs never cross classes), ranked with executable rows (book-priced netApr) first by netApr, then indicative rows by gross annualized funding differential (apr); pairs whose two legs' prices differ by more than 20% are indicative (likely different instruments). Spread uses real books where available (spreadSource: book) or reference prices (reference); netApr is present only on book-priced rows; unknown spread/OI/volume are null, never zero.",
632
947
  schema: arbitrageCrossExchangeSchema,
633
948
  apiPath: "/api/v1/arbitrage/cross-exchange",
634
949
  fallbackPath: "/api/arbitrage/cross-exchange",
@@ -656,35 +971,21 @@ export const TOOL_DEFS = [
656
971
  },
657
972
  {
658
973
  name: "get_arbitrage_cex_spot_transfer",
659
- description: "Get live CEX spot-transfer arbitrage rows after common network, fee, status, depth, slippage, and spread-lifetime checks.",
974
+ description: "Get live CEX spot-transfer arbitrage rows after common network and token-contract matching, fee, status, depth, slippage, and spread-lifetime checks.",
660
975
  schema: cexSpotTransferScannerSchema,
661
976
  apiPath: "/api/v1/arbitrage/cex-spot-transfer",
662
977
  fallbackPath: "/api/arbitrage/cex-spot-transfer",
663
978
  },
664
979
  {
665
980
  name: "get_heatmap",
666
- description: "Get market heatmap data for coins, narratives, or ecosystems. Visualizes relative performance across the market.",
981
+ description: "Get market heatmap data for coins, narratives, or ecosystems. The legacy top-100 slug returns the Top 50 universe; aggregate modes expose complete or partial category coverage.",
667
982
  schema: heatmapSchema,
668
983
  apiPath: "/api/v1/heatmap/data",
669
984
  fallbackPath: "/api/heatmap/data",
670
985
  },
671
- {
672
- name: "get_dexscreener_data",
673
- description: "Get live DEX Screener rows by category, network, or search phrase. Use this for pair-level discovery across DEX markets. Requires API key (SHARPE_API_KEY) — not available on the free tier.",
674
- schema: dexScreenerDataSchema,
675
- apiPath: "/api/v1/dexscreener/data",
676
- fallbackPath: "/api/v1/dexscreener/data",
677
- },
678
- {
679
- name: "get_dexscreener_security",
680
- description: "Get token contract security signals for a DEX Screener token by address and chain ID.",
681
- schema: dexScreenerSecuritySchema,
682
- apiPath: "/api/v1/dexscreener/security",
683
- fallbackPath: "/api/dexscreener/security",
684
- },
685
986
  {
686
987
  name: "get_token_scanner",
687
- description: "Run read-only token scanner modes: hot tokens, new runners, alpha drops, AI tokens, or top new pairs. Requires API key (SHARPE_API_KEY) — not available on the free tier.",
988
+ description: "Run read-only token scanner modes for hot tokens, new runners, alpha drops, AI tokens, and top-new pairs. Responses expose source coverage; valuation and risk fields stay null when unavailable. Requires an API key (SHARPE_API_KEY; the free tier works). No keyless fallback.",
688
989
  schema: tokenScannerSchema,
689
990
  apiPath: "/api/v1/token-scanner/scan",
690
991
  fallbackPath: "/api/v1/token-scanner/scan",
@@ -726,14 +1027,14 @@ export const TOOL_DEFS = [
726
1027
  },
727
1028
  {
728
1029
  name: "get_memecoin_launches",
729
- description: "Get recently launched memecoin pairs screened by age, liquidity, volume, and transaction count.",
1030
+ description: "Get recently launched tokens from recognized memecoin launchpads, screened by age, liquidity, volume, and transactions. Generic new DEX pairs are excluded; response includes complete/partial chain coverage.",
730
1031
  schema: memecoinLaunchesSchema,
731
1032
  apiPath: "/api/v1/memecoins/launches",
732
1033
  fallbackPath: "/api/memecoins/launches",
733
1034
  },
734
1035
  {
735
1036
  name: "get_stablecoins",
736
- description: "Get stablecoin overview, detail, or yield data including market cap, peg deviation, mechanism mix, and chain supply. Requires API key (SHARPE_API_KEY) — not available on the free tier.",
1037
+ description: "Get stablecoin overview, detail, or yield data including nominal supply at peg, marked value, nullable peg metrics, mechanism mix, chain supply, velocity coverage, freshness, and APY risk flags. Requires an API key (SHARPE_API_KEY; the free tier works). No keyless fallback.",
737
1038
  schema: stablecoinsSchema,
738
1039
  apiPath: "/api/v1/stablecoins/data",
739
1040
  fallbackPath: "/api/v1/stablecoins/data",
@@ -747,63 +1048,65 @@ export const TOOL_DEFS = [
747
1048
  },
748
1049
  {
749
1050
  name: "get_web_traffic",
750
- description: "Get attention rankings, search trends, social snapshots, or market-level traffic signals for exchanges, coins, and narratives.",
1051
+ description: "Get attention rankings, social snapshots, or market-level signals for coins and narratives, plus CoinGecko trust-score rankings for exchanges.",
751
1052
  schema: webTrafficSchema,
752
1053
  apiPath: "/api/v1/web-traffic/data",
753
1054
  fallbackPath: "/api/web-traffic/data",
754
1055
  },
755
- {
756
- name: "get_katana",
757
- description: "Get Katana Network TVL, token, holder, sentiment, pool, mover, and ecosystem analytics.",
758
- schema: katanaSchema,
759
- apiPath: "/api/v1/katana/data",
760
- fallbackPath: "/api/products/katana/data",
761
- },
762
1056
  {
763
1057
  name: "get_news",
764
- description: "Get aggregated crypto news articles from multiple sources, with optional filtering by category or coin. Articles are deduplicated so only the best source per story is shown.",
1058
+ description: "Get aggregated crypto news articles from multiple sources, with optional title search and filtering by category or coin. Articles are deduplicated so only the best source per story is shown.",
765
1059
  schema: newsSchema,
766
1060
  apiPath: "/api/v1/news/feed",
767
1061
  fallbackPath: "/api/news/feed",
768
1062
  },
769
- {
770
- name: "get_curated_news",
771
- description: "Get AI-curated crypto news highlights across crypto, AI, markets, and geopolitics. Updated every 15 minutes.",
772
- schema: curatedNewsSchema,
773
- apiPath: "/api/v1/news/curated",
774
- fallbackPath: "/api/news/curated",
775
- },
776
1063
  {
777
1064
  name: "get_market_overview",
778
- description: "Get a broad market overview including total market cap, 24h volume, BTC dominance, top gainers/losers, and trending coins.",
1065
+ description: "Get BTC and ETH price/change, total crypto market cap and 24h volume, BTC dominance, market-cap change, and the Fear & Greed Index. Auxiliary source fields may be null when unavailable.",
779
1066
  schema: marketOverviewSchema,
780
1067
  apiPath: "/api/v1/tracker/market-overview",
781
1068
  fallbackPath: "/api/tracker/market-overview",
782
1069
  },
783
1070
  {
784
1071
  name: "get_price_prediction",
785
- description: "Get ML-based price prediction data for a specified coin, including predicted price targets and confidence intervals.",
1072
+ description: "Get deterministic directional scores and sub-signal breakdowns for a coin. Optional 7D/30D values are volatility-scaled heuristic scenarios, not calibrated targets or probabilities.",
786
1073
  schema: pricePredictionSchema,
787
1074
  apiPath: "/api/v1/price-prediction/data",
788
1075
  fallbackPath: "/api/price-prediction/data",
789
1076
  },
790
1077
  {
791
1078
  name: "search_market_cap",
792
- description: "Search for coins by name or ticker. Returns matching coins with market cap, price, and 24h change.",
1079
+ description: "Search crypto and supported TradFi assets by name or ticker. Returns matching assets with asset class, market cap, and nullable price, 24h change, FDV, ATH, and estimated ATH market cap fields.",
793
1080
  schema: searchMarketCapSchema,
794
1081
  apiPath: "/api/v1/market-cap/search",
795
1082
  fallbackPath: "/api/market-cap/search",
796
1083
  },
797
1084
  {
798
1085
  name: "get_gem_finder",
799
- description: "Get a curated list of low-cap, high-potential tokens identified by the gem finder algorithm.",
1086
+ description: "Get market-ranked token rows with market cap, volume, price and market-cap change, circulating supply, ATH and ATL history, nullable FDV/MCap, the preferred chain plus every supported chain the asset is deployed on (chainSlugs), observed exchange availability, quote freshness, and enrichment coverage. Delisted or halted markets whose upstream quote is over 24 hours old are excluded rather than reporting a weeks-old price change as current. Ambiguous same-ticker assets do not receive symbol-only exchange attribution. No wallet signal or proprietary score is returned.",
800
1087
  schema: gemFinderSchema,
801
1088
  apiPath: "/api/v1/gem-finder/data",
802
1089
  fallbackPath: "/api/gem-finder/data",
1090
+ postProcess: (data, args) => {
1091
+ // Authenticated v1 calls are already an array with pagination reattached
1092
+ // by the runtime. The free fallback returns the complete cache payload,
1093
+ // so apply the same snapshot-bound paging semantics client-side.
1094
+ if (!data ||
1095
+ typeof data !== "object" ||
1096
+ Array.isArray(data) ||
1097
+ !Array.isArray(data.coins)) {
1098
+ return data;
1099
+ }
1100
+ const limit = typeof args.limit === "number" && Number.isInteger(args.limit)
1101
+ ? args.limit
1102
+ : 100;
1103
+ const cursor = typeof args.cursor === "string" ? args.cursor : undefined;
1104
+ return paginateSnapshotPayload(data, limit, cursor);
1105
+ },
803
1106
  },
804
1107
  {
805
1108
  name: "get_derivatives_overview",
806
- description: "Get a derivatives market overview including aggregated open interest, funding rates, and volume across top perpetual futures exchanges. Requires API key (SHARPE_API_KEY) — not available on the free tier.",
1109
+ description: "Get a derivatives market overview including aggregated open interest, 8-hour-equivalent funding rates, top coins by open interest, and exchange/coin counts. Requires an API key (SHARPE_API_KEY; the free tier works). No keyless fallback.",
807
1110
  schema: derivativesOverviewSchema,
808
1111
  apiPath: "/api/v1/market/derivatives-overview",
809
1112
  fallbackPath: "/api/v1/market/derivatives-overview",
@@ -817,21 +1120,21 @@ export const TOOL_DEFS = [
817
1120
  },
818
1121
  {
819
1122
  name: "get_exchange_listings",
820
- description: "Get the New Listings hub payload — weekly + monthly aggregated counts by narrative and exchange, plus the last-90-day recent listings. Optionally filter by narrative slug or single exchange. Useful for tracking where venues are adding listing slots.",
1123
+ description: "Get the New Listings hub payload: weekly + monthly aggregated counts by narrative and exchange, plus the last-90-day recent listings. Optionally filter by narrative slug or single exchange. Useful for tracking where venues are adding listing slots.",
821
1124
  schema: listingsDataSchema,
822
1125
  apiPath: "/api/v1/listings/data",
823
1126
  fallbackPath: "/api/listings/data",
824
1127
  },
825
1128
  {
826
1129
  name: "get_recent_listings",
827
- description: "Get a flat feed of recent token listings. Defaults remain backward-compatible with the legacy spot listing feed, and optional event filters can narrow by venue type, market type, event type, status, and source confidence.",
1130
+ description: "Get a flat feed of recent token listings. Defaults remain backward-compatible with the legacy spot listing feed. Event filters add source-observation freshness, partial status, and provider coverage so agents can reject incomplete opportunity scans.",
828
1131
  schema: listingsRecentSchema,
829
1132
  apiPath: "/api/v1/listings/recent",
830
1133
  fallbackPath: "/api/listings/recent",
831
1134
  },
832
1135
  {
833
1136
  name: "get_listing_events",
834
- description: "Get canonical listing lifecycle events across CEXs and Perp DEXs, including Spot listings, Perp listings, delistings, suspensions, resumptions, scheduled trading starts, source URLs, and source confidence.",
1137
+ description: "Get canonical listing lifecycle events across CEXs and Perp DEXs, including source URLs, confidence, real source-observation freshness, partial status, and per-provider coverage. Pass the opaque cursor through unchanged.",
835
1138
  schema: listingsEventsSchema,
836
1139
  apiPath: "/api/v1/listings/events",
837
1140
  fallbackPath: "/api/listings/events",
@@ -845,17 +1148,19 @@ export const TOOL_DEFS = [
845
1148
  },
846
1149
  {
847
1150
  name: "get_insider_selling",
848
- description: "Get insider selling pressure signals across crypto assets. Scores require cross-venue negative funding and combine OI, basis, CVD, sell-side flow, and supporting wallet/supply context.",
1151
+ description: "Get insider selling pressure signals across crypto assets. Scores require cross-venue negative funding and combine OI, basis, CVD, sell-side flow, and supporting wallet/supply context; read signal_coverage_pct because partial-input scores are conservative lower bounds.",
849
1152
  schema: signalScoreSchema,
850
1153
  apiPath: "/api/v1/insider-selling/data",
851
1154
  fallbackPath: "/api/insider-selling/data",
1155
+ postProcess: filterSignalScores,
852
1156
  },
853
1157
  {
854
1158
  name: "get_pump_dump",
855
- description: "Get pump-and-dump manipulation risk signals across crypto assets. Scores combine derivatives pressure, holder concentration, liquidity quality, DEX flow, and price behavior.",
1159
+ description: "Get pump-and-dump manipulation risk signals across crypto assets. Scores combine derivatives pressure, holder concentration, liquidity quality, DEX flow, and price behavior; read signal_coverage_pct because partial-input scores are conservative lower bounds.",
856
1160
  schema: pumpDumpSchema,
857
1161
  apiPath: "/api/v1/pump-dump/data",
858
1162
  fallbackPath: "/api/pump-dump/data",
1163
+ postProcess: filterSignalScores,
859
1164
  },
860
1165
  ];
861
1166
  //# sourceMappingURL=tools.js.map