@coinrithm/mcp-trading 0.7.19 → 0.7.20

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.
@@ -50,37 +50,37 @@ const PRESETS = {
50
50
  modelFail: 5,
51
51
  },
52
52
  };
53
- const THESIS_BODY = `# Momentum Futures — strategy
54
-
55
- You operate a CoinRithm **paper-trading** futures account (50,000 virtual mUSD).
56
- Everything here is simulated; it is not financial advice and never touches real
57
- money. Edit this prose freely (any language) — it is your agent's borders.
58
-
59
- ## Each cycle
60
-
61
- 1. Ground yourself: read your portfolio and open positions first. Never assume
62
- balances or what is already open.
63
- 2. Scan the watchlist. A candidate is a coin whose short and medium momentum
64
- agree (both up, or both down) and is not already an open position.
65
- 3. Pick the strongest candidate and commit when the read is clear — even a
66
- moderate-confidence one — sized small with a stop. Skip only when the signals
67
- contradict or the data is stale; a quiet tape where your edge is still real
68
- is an act, not a skip.
69
- 4. Quote before you open. Read the liquidation price and confirm it is sane. If
70
- the quote is not eligible, relay the reason and stop.
71
- 5. Open small and protected: enter in the trend direction and set a stop-loss at
72
- open. Place the take-profit a touch wider than the stop.
73
- 6. Stay in sync: poll your trades for any stop / take-profit / liquidation that
74
- fired while you were not looking, and react to what actually happened.
75
-
76
- The hard caps (leverage, margin, watchlist) live in the config blocks above and
53
+ const THESIS_BODY = `# Momentum Futures — strategy
54
+
55
+ You operate a CoinRithm **paper-trading** futures account (50,000 virtual mUSD).
56
+ Everything here is simulated; it is not financial advice and never touches real
57
+ money. Edit this prose freely (any language) — it is your agent's borders.
58
+
59
+ ## Each cycle
60
+
61
+ 1. Ground yourself: read your portfolio and open positions first. Never assume
62
+ balances or what is already open.
63
+ 2. Scan the watchlist. A candidate is a coin whose short and medium momentum
64
+ agree (both up, or both down) and is not already an open position.
65
+ 3. Pick the strongest candidate and commit when the read is clear — even a
66
+ moderate-confidence one — sized small with a stop. Skip only when the signals
67
+ contradict or the data is stale; a quiet tape where your edge is still real
68
+ is an act, not a skip.
69
+ 4. Quote before you open. Read the liquidation price and confirm it is sane. If
70
+ the quote is not eligible, relay the reason and stop.
71
+ 5. Open small and protected: enter in the trend direction and set a stop-loss at
72
+ open. Place the take-profit a touch wider than the stop.
73
+ 6. Stay in sync: poll your trades for any stop / take-profit / liquidation that
74
+ fired while you were not looking, and react to what actually happened.
75
+
76
+ The hard caps (leverage, margin, watchlist) live in the config blocks above and
77
77
  are enforced by the runner — change them there, not in this prose.`;
78
- const PERSONA_STUB = `# Persona
79
-
80
- Decisive and in character. Acts on a clear read — even a moderate-confidence one
81
- — sized small and protected with a stop, and skips only when the read is
82
- contradictory or the data is stale. States its reasoning plainly in its own
83
- voice, and never frames paper results as real-money advice.
78
+ const PERSONA_STUB = `# Persona
79
+
80
+ Decisive and in character. Acts on a clear read — even a moderate-confidence one
81
+ — sized small and protected with a stop, and skips only when the read is
82
+ contradictory or the data is stale. States its reasoning plainly in its own
83
+ voice, and never frames paper results as real-money advice.
84
84
  `;
85
85
  export function buildAgentObject(name, preset) {
86
86
  const p = PRESETS[preset];
@@ -5,7 +5,7 @@ export declare const COINRITHM_API: {
5
5
  readonly kind: "coinrithm-agent-api";
6
6
  readonly baseUrl: "https://api.coinrithm.com";
7
7
  readonly mcpUrl: "https://mcp.coinrithm.com/mcp";
8
- readonly openapiVersion: "1.9.0";
8
+ readonly openapiVersion: "1.11.0";
9
9
  readonly mcpPackage: "@coinrithm/mcp-trading";
10
10
  readonly mcpVersion: string;
11
11
  };
@@ -20,7 +20,7 @@ export const COINRITHM_API = {
20
20
  mcpUrl: "https://mcp.coinrithm.com/mcp",
21
21
  // The API CONTRACT version (openapi.yaml info.version). Versioned independently
22
22
  // from the npm package below — hand-bump this when the OpenAPI contract changes.
23
- openapiVersion: "1.9.0",
23
+ openapiVersion: "1.11.0",
24
24
  mcpPackage: "@coinrithm/mcp-trading",
25
25
  mcpVersion: PACKAGE_VERSION,
26
26
  };
package/dist/client.d.ts CHANGED
@@ -90,7 +90,52 @@ export declare class CoinRithmClient {
90
90
  }): Promise<ApiResult>;
91
91
  getPublicPmCanonicalDetail(key: string): Promise<ApiResult>;
92
92
  getPublicPmVolumeHistory(): Promise<ApiResult>;
93
+ getPublicPmPriceHistory(source: string, slug: string, query?: {
94
+ interval?: string;
95
+ }): Promise<ApiResult>;
96
+ getPublicPmOrderBook(source: string, slug: string, query?: {
97
+ outcomeExternalMarketId?: string;
98
+ }): Promise<ApiResult>;
99
+ listPublicPmResolved(query?: {
100
+ limit?: number;
101
+ offset?: number;
102
+ fiat?: string;
103
+ source?: string;
104
+ minVolume?: number;
105
+ year?: number;
106
+ month?: number;
107
+ }): Promise<ApiResult>;
108
+ listPublicPmExpiring(query?: {
109
+ limit?: number;
110
+ offset?: number;
111
+ fiat?: string;
112
+ days?: number;
113
+ source?: string;
114
+ }): Promise<ApiResult>;
115
+ listPublicPmSurprises(query?: {
116
+ limit?: number;
117
+ offset?: number;
118
+ fiat?: string;
119
+ source?: string;
120
+ window?: string;
121
+ }): Promise<ApiResult>;
122
+ getPublicInstrumentMarkets(type: string, id: string, query?: {
123
+ horizon?: string;
124
+ includeShortTerm?: boolean;
125
+ limit?: number;
126
+ }): Promise<ApiResult>;
127
+ getPublicTradfiReferences(): Promise<ApiResult>;
128
+ getPublicTradfiReference(ticker: string, query?: {
129
+ window?: string;
130
+ }): Promise<ApiResult>;
93
131
  getPublicCryptoMovers(direction: "gainers" | "losers", limit?: number): Promise<ApiResult>;
132
+ getPublicCoin(slug: string, query?: {
133
+ fiat?: string;
134
+ }): Promise<ApiResult>;
135
+ getPublicCoinCandles(slug: string, query?: {
136
+ range?: string;
137
+ fiat?: string;
138
+ }): Promise<ApiResult>;
94
139
  whoami(apiKey?: string, agentTrace?: AgentTrace): Promise<ApiResult>;
95
140
  getPortfolio(query?: {
96
141
  fiat?: string;
package/dist/client.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { CALLER_IP_HEADER, callerIp } from "./requestContext.js";
1
2
  import { PM_PAPER_V2_BASE, } from "./pmPaperV2.js";
2
3
  // Thin HTTP client for the CoinRithm agent surface (/api/agent/*).
3
4
  //
@@ -29,7 +30,8 @@ function resolveBaseUrl() {
29
30
  }
30
31
  // stdio path (single user). The key is OPTIONAL since 2026-10-16 (audit):
31
32
  // without one the server starts in data-only mode, exactly like the hosted
32
- // server: keyless tools (pm_data_*, get_arena_*, get_crypto_movers) work and
33
+ // server: keyless tools (pm_data_*, instrument_markets, tradfi_references,
34
+ // crypto_price, get_arena_*, get_crypto_movers) work and
33
35
  // every keyed tool returns the structured missing_api_key result. Requiring a
34
36
  // key blocked the one-line `npx -y @coinrithm/mcp-trading` data install.
35
37
  export function loadConfig() {
@@ -122,8 +124,9 @@ export class CoinRithmClient {
122
124
  message: "No API key for this request. Mint a free key at " +
123
125
  `${API_KEY_MINT_URL}, then on the hosted MCP send ` +
124
126
  "`Authorization: Bearer crk_live_…`, or for the local server " +
125
- "set COINRITHM_API_KEY. Public data tools " +
126
- "(pm_data_*, get_arena_*, get_crypto_movers) need no key.",
127
+ "set COINRITHM_API_KEY. Public data tools (pm_data_*, " +
128
+ "instrument_markets, tradfi_references, crypto_price, " +
129
+ "get_arena_*, get_crypto_movers) need no key.",
127
130
  },
128
131
  };
129
132
  }
@@ -142,6 +145,9 @@ export class CoinRithmClient {
142
145
  applyAgentTraceHeaders(headers, opts.agentTrace ?? traceFromBody(opts.body));
143
146
  if (opts.body !== undefined)
144
147
  headers["Content-Type"] = "application/json";
148
+ const ip = callerIp();
149
+ if (ip)
150
+ headers[CALLER_IP_HEADER] = ip;
145
151
  let res;
146
152
  try {
147
153
  res = await fetch(url.toString(), {
@@ -200,9 +206,13 @@ export class CoinRithmClient {
200
206
  }
201
207
  let res;
202
208
  try {
209
+ const ip = callerIp();
203
210
  res = await fetch(url.toString(), {
204
211
  method: "GET",
205
- headers: { Accept: "application/json" },
212
+ headers: {
213
+ Accept: "application/json",
214
+ ...(ip ? { [CALLER_IP_HEADER]: ip } : {}),
215
+ },
206
216
  signal: AbortSignal.timeout(UPSTREAM_TIMEOUT_MS),
207
217
  });
208
218
  }
@@ -302,6 +312,38 @@ export class CoinRithmClient {
302
312
  getPublicPmVolumeHistory() {
303
313
  return this.publicRequest("/api/prediction-markets/volume-history");
304
314
  }
315
+ // Native venue price history for ONE outcome (Polymarket and Kalshi; other
316
+ // venues answer 200 with points: []). Points are {t: epoch ms, p: 0-1}.
317
+ getPublicPmPriceHistory(source, slug, query) {
318
+ return this.publicRequest(`/api/prediction-markets/event/${encodeURIComponent(source)}/${encodeURIComponent(slug)}/price-history`, query);
319
+ }
320
+ // Live order book for the same outcome price-history describes, or the
321
+ // outcome named by outcomeExternalMarketId. Prices are 0-1 fractions.
322
+ getPublicPmOrderBook(source, slug, query) {
323
+ return this.publicRequest(`/api/prediction-markets/event/${encodeURIComponent(source)}/${encodeURIComponent(slug)}/orderbook`, query);
324
+ }
325
+ listPublicPmResolved(query) {
326
+ return this.publicRequest("/api/prediction-markets/resolved", query);
327
+ }
328
+ listPublicPmExpiring(query) {
329
+ return this.publicRequest("/api/prediction-markets/expiring", query);
330
+ }
331
+ listPublicPmSurprises(query) {
332
+ return this.publicRequest("/api/prediction-markets/surprises", query);
333
+ }
334
+ // ---- public instrument and TradFi reference data (no key required) ----
335
+ // Prediction-market strike ladders written on one instrument's price. The
336
+ // API returns 400 for any query parameter other than these three.
337
+ getPublicInstrumentMarkets(type, id, query) {
338
+ return this.publicRequest(`/api/instruments/${encodeURIComponent(type)}/${encodeURIComponent(id)}/markets`, query);
339
+ }
340
+ // The catalog accepts no query parameters (any one returns 400).
341
+ getPublicTradfiReferences() {
342
+ return this.publicRequest("/api/market/references");
343
+ }
344
+ getPublicTradfiReference(ticker, query) {
345
+ return this.publicRequest(`/api/market/references/${encodeURIComponent(ticker)}`, query);
346
+ }
305
347
  // ---- public crypto data (no key required) ----
306
348
  // Top 24h movers across the tracked coin universe (user feature request,
307
349
  // 2026-08-18: agents previously could only analyze manually-added pairs).
@@ -312,6 +354,16 @@ export class CoinRithmClient {
312
354
  ? "/api/coins/top-losers"
313
355
  : "/api/coins/top-gainers", { limit });
314
356
  }
357
+ // One coin's profile by slug: price in USD and `fiat`, 24h change, market
358
+ // cap, volume and the live-price as-of (priceUpdatedAt, priceStale).
359
+ // Numerics are decimal strings. An unknown slug is 404 {message}.
360
+ getPublicCoin(slug, query) {
361
+ return this.publicRequest(`/api/coins/${encodeURIComponent(slug)}`, query);
362
+ }
363
+ // OHLC bars by slug; `range` fixes lookback and resolution (1H = 60 x 1m).
364
+ getPublicCoinCandles(slug, query) {
365
+ return this.publicRequest(`/api/coins/${encodeURIComponent(slug)}/candles`, query);
366
+ }
315
367
  // Every method takes an optional trailing `apiKey` (the per-request key for
316
368
  // the multi-user HTTP path). When omitted, the constructor key (stdio) is used.
317
369
  // ---- reads (scope: read) ----
package/dist/http.js CHANGED
@@ -45,6 +45,7 @@ import { SERVER_VERSION } from "./version.js";
45
45
  import { observeHttpCompletion, } from "./httpCompletion.js";
46
46
  import { createMcpAdmission } from "./httpLimits.js";
47
47
  import { installGracefulShutdown } from "./httpShutdown.js";
48
+ import { runWithCaller } from "./requestContext.js";
48
49
  // "/mcp" immediately followed by the markdown "](" of a pasted link, literal or
49
50
  // percent-encoded. Anything else under /mcp is not this family.
50
51
  export const MALFORMED_MCP_LINK = /^\/mcp(?:\]|%5d)(?:\(|%28)/i;
@@ -84,7 +85,7 @@ export function createHttpApp(client, options = {}) {
84
85
  res.json({
85
86
  service: "CoinRithm MCP",
86
87
  version: SERVER_VERSION,
87
- description: "Model Context Protocol server for CoinRithm: keyless prediction-market data tools across 12 venues (pm_data_*), keyless crypto movers and Agent Arena reads, plus crypto market context and paper-trading tools (spot, futures, prediction markets) with a crk_live_ API key. Opt-in PM paper v2 and spot reference tools are off unless the server enables them. Paper only, never real money.",
88
+ description: "Model Context Protocol server for CoinRithm: keyless prediction-market data tools across 12 venues (pm_data_*), keyless strike ladders (instrument_markets), TradFi reference prices (tradfi_references), crypto prices (crypto_price), crypto movers and Agent Arena reads, plus crypto market context and paper-trading tools (spot, futures, prediction markets) with a crk_live_ API key. Opt-in PM paper v2 and spot reference tools are off unless the server enables them. Paper only, never real money.",
88
89
  endpoint: {
89
90
  url: "https://mcp.coinrithm.com/mcp",
90
91
  method: "POST",
@@ -171,7 +172,9 @@ export function createHttpApp(client, options = {}) {
171
172
  completions.get(res)?.attach(transport);
172
173
  // The transport reads req.headers (→ extra.requestInfo) and req.auth
173
174
  // (→ extra.authInfo); tools.ts picks up the caller's key from there.
174
- await transport.handleRequest(req, res, req.body);
175
+ // Upstream calls made while handling it carry the caller's own
176
+ // address (requestContext.ts), not the container's.
177
+ await runWithCaller(req.headers["cf-connecting-ip"], () => transport.handleRequest(req, res, req.body));
175
178
  }
176
179
  catch (err) {
177
180
  log("http request error:", err);
@@ -1,6 +1,6 @@
1
1
  import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
3
- export declare const COMPLETION_TOOL_NAMES: readonly ["whoami", "get_portfolio", "get_wallet", "list_open_orders", "get_positions", "resolve_symbol", "get_equity_curve", "get_my_trades", "get_market_context", "get_candles", "get_news", "discover_pm_markets", "get_performance", "get_agent_ledger", "export_agent_ledger", "export_run_evidence", "get_arena_leaderboard", "get_arena_agent", "futures_quote", "pm_quote", "spot_quote", "place_spot_order", "cancel_spot_order", "open_futures_position", "set_futures_sl_tp", "close_futures_position", "open_pm_position", "report_pm_opportunity", "pm_data_overview", "pm_data_sources", "pm_data_sources_health", "pm_data_events", "pm_data_event", "pm_data_whales", "pm_data_whale_wallets", "pm_data_whale_wallet", "pm_data_disagreements", "pm_data_calibration", "pm_data_canonical", "pm_data_volume_history", "get_crypto_movers"];
3
+ export declare const COMPLETION_TOOL_NAMES: readonly ["whoami", "get_portfolio", "get_wallet", "list_open_orders", "get_positions", "resolve_symbol", "get_equity_curve", "get_my_trades", "get_market_context", "get_candles", "get_news", "discover_pm_markets", "get_performance", "get_agent_ledger", "export_agent_ledger", "export_run_evidence", "get_arena_leaderboard", "get_arena_agent", "futures_quote", "pm_quote", "spot_quote", "place_spot_order", "cancel_spot_order", "open_futures_position", "set_futures_sl_tp", "close_futures_position", "open_pm_position", "report_pm_opportunity", "pm_data_overview", "pm_data_sources", "pm_data_sources_health", "pm_data_events", "pm_data_event", "pm_data_whales", "pm_data_whale_wallets", "pm_data_whale_wallet", "pm_data_disagreements", "pm_data_calibration", "pm_data_canonical", "pm_data_volume_history", "pm_data_price_history", "pm_data_orderbook", "pm_data_resolved", "pm_data_expiring", "pm_data_surprises", "get_crypto_movers", "instrument_markets", "tradfi_references", "crypto_price"];
4
4
  type Operation = "initialize" | "tools_list" | "tools_call" | "notification" | "other" | "invalid";
5
5
  type Outcome = "result" | "tool_error" | "protocol_error" | "no_response" | "not_applicable";
6
6
  type ToolName = (typeof COMPLETION_TOOL_NAMES)[number] | "unknown" | null;
@@ -43,7 +43,15 @@ export const COMPLETION_TOOL_NAMES = [
43
43
  "pm_data_calibration",
44
44
  "pm_data_canonical",
45
45
  "pm_data_volume_history",
46
+ "pm_data_price_history",
47
+ "pm_data_orderbook",
48
+ "pm_data_resolved",
49
+ "pm_data_expiring",
50
+ "pm_data_surprises",
46
51
  "get_crypto_movers",
52
+ "instrument_markets",
53
+ "tradfi_references",
54
+ "crypto_price",
47
55
  ];
48
56
  const toolNames = new Set(COMPLETION_TOOL_NAMES);
49
57
  const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value)
@@ -6,7 +6,7 @@ import { PAPER_NOTE } from "./tools.js";
6
6
  // paper disclaimer on 25 tools, audit 2026-09-29).
7
7
  export const SERVER_INSTRUCTIONS = [
8
8
  "CoinRithm MCP: crypto and prediction-market data from 12 venues, plus paper trading.",
9
- "Start keyless: the pm_data_* tools, get_arena_leaderboard, get_arena_agent and get_crypto_movers need no API key.",
9
+ "Start keyless: the pm_data_* tools, instrument_markets, tradfi_references, crypto_price, get_arena_leaderboard, get_arena_agent and get_crypto_movers need no API key.",
10
10
  "pm_data_events returns open markets by default; pass status=closed or status=all for history.",
11
11
  `Tools that read or act on an account need a key. The user mints one at ${API_KEY_MINT_URL}; start with a read-only key.`,
12
12
  "Every tool returns { httpStatus, ok, ledgerEventId, ledgerStatus, body }: httpStatus is CoinRithm's HTTP status (0 = network error), ok means 2xx, body is the parsed response.",
@@ -0,0 +1,5 @@
1
+ export declare const CALLER_IP_HEADER = "cf-connecting-ip";
2
+ /** Runs `fn` with the caller's address (if it is a valid IP) in scope. */
3
+ export declare function runWithCaller<T>(rawIp: string | string[] | undefined, fn: () => T): T;
4
+ /** The current request's caller address, or undefined (stdio, no header). */
5
+ export declare const callerIp: () => string | undefined;
@@ -0,0 +1,25 @@
1
+ // Per-HTTP-request context for the hosted MCP (audit 2026-10-16, fresh kit
2
+ // audit P1-1). The hosted server calls the API inside the Docker network
3
+ // (http://api:4000), where the API's keyless rate limit buckets by
4
+ // cf-connecting-ip and otherwise by the socket address. Without this every
5
+ // anonymous MCP user shared the MCP container's one bucket (300 requests per
6
+ // 5 minutes), so one busy client could 429 everyone else.
7
+ //
8
+ // http.ts records the caller's Cloudflare-set address for the duration of the
9
+ // request; the client forwards it as cf-connecting-ip on upstream calls. The
10
+ // API trusts that header only because its origin accepts nothing but
11
+ // Cloudflare and this internal network; the value here is the one Cloudflare
12
+ // set on the MCP request, re-validated as an IP. stdio has no context and
13
+ // sends nothing extra.
14
+ import { AsyncLocalStorage } from "node:async_hooks";
15
+ import { isIP } from "node:net";
16
+ const storage = new AsyncLocalStorage();
17
+ export const CALLER_IP_HEADER = "cf-connecting-ip";
18
+ /** Runs `fn` with the caller's address (if it is a valid IP) in scope. */
19
+ export function runWithCaller(rawIp, fn) {
20
+ const value = Array.isArray(rawIp) ? rawIp[0] : rawIp;
21
+ const ip = value?.trim();
22
+ return storage.run({ clientIp: ip && isIP(ip) ? ip : undefined }, fn);
23
+ }
24
+ /** The current request's caller address, or undefined (stdio, no header). */
25
+ export const callerIp = () => storage.getStore()?.clientIp;
package/dist/tools.d.ts CHANGED
@@ -27,4 +27,48 @@ export declare function compactPublicCryptoMovers(data: unknown): unknown;
27
27
  * ~466KB to ~48KB.
28
28
  */
29
29
  export declare function compactPublicPmDisagreements(data: unknown): unknown;
30
+ /**
31
+ * Evenly spaced subset of a time series that always keeps the first and the
32
+ * last point. Values are never rewritten or interpolated.
33
+ */
34
+ export declare function samplePoints(points: unknown[], max: number): unknown[];
35
+ /** Price history for one outcome, with the point series bounded to maxPoints. */
36
+ export declare function compactPublicPmPriceHistory(data: unknown, maxPoints: number): unknown;
37
+ /** Order book with each side cut to its best `depth` levels as served. */
38
+ export declare function compactPublicPmOrderBook(data: unknown, depth: number): unknown;
39
+ /** Resolved archive and surprise pages: rows reduced to settled summaries. */
40
+ export declare function compactPublicPmSettled(data: unknown): unknown;
41
+ /** Expiring page: each row is a full event plus expiresInMs. */
42
+ export declare function compactPublicPmExpiring(data: unknown): unknown;
43
+ /**
44
+ * Strike ladders without per-contract venue ids, parser details and URLs.
45
+ * detail=full returns the untouched record (externalMarketId included).
46
+ * Verified live 2026-10-16: bitcoin, 10 ladders, 174 contracts went from
47
+ * about 92 KB to about 40 KB.
48
+ */
49
+ export declare function compactInstrumentMarkets(data: unknown): unknown;
50
+ export declare const TRADFI_ASSET_CLASSES: readonly ["equity", "etf", "metal", "commodity", "fx"];
51
+ /** TradFi catalog filtered by asset class and paged on the MCP side. */
52
+ export declare function compactTradfiCatalog(data: unknown, opts: {
53
+ assetClass?: string;
54
+ limit: number;
55
+ offset: number;
56
+ }): unknown;
57
+ /** TradFi detail with its 15-minute history bounded to maxPoints. */
58
+ export declare function compactTradfiReference(data: unknown, maxPoints: number): unknown;
59
+ export declare const COIN_CANDLE_RANGES: readonly ["1H", "1D", "1W", "1M", "3M", "1Y"];
60
+ /**
61
+ * Compact coin profile from GET /api/coins/{slug}: price, change, market cap,
62
+ * volume and the live-price as-of. The profile, not /price, because its
63
+ * priceUpdatedAt is the live-price write time (probe 2026-10-16: /price
64
+ * reported dead-pair coins as 0.6 h old while the profile said weeks).
65
+ */
66
+ export declare function compactCryptoPrice(data: unknown, fiat: string): unknown;
67
+ /**
68
+ * Bars from GET /api/coins/{slug}/candles, merged into at most maxPoints
69
+ * consecutive groups (open of the first bar, high and low across the group,
70
+ * close of the last). `v` is dropped: it is a rolling 24h volume observed at
71
+ * each bar, not the volume traded in it, so it cannot be merged.
72
+ */
73
+ export declare function compactCoinCandles(data: unknown, maxPoints: number): unknown;
30
74
  export declare function registerTools(server: McpServer, client: CoinRithmClient): void;