@candledottv/mcp 0.10.20 → 0.10.21

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 CHANGED
@@ -57,8 +57,10 @@ Both take decimal amounts, never raw base units, and resolve the scale themselve
57
57
 
58
58
  Both launch tools also take `mode: "exp-custom"` with `curve: { virtualQuote, threshold }`: a
59
59
  Solana curve with your own V and G, in whole SOL or CNDL as decimal strings, inside limits the
60
- API enforces and each tool's description states. Dry-run first: the dry run returns the curve's
61
- terms. It works only where the deployment has switched it on. See
60
+ API enforces and each tool's description states. Any `exp-` mode takes `creatorFeeBps` (0, 100 or
61
+ 200). Dry-run first: the dry run returns the launch preview (`curveTerms`: the curve's terms, the
62
+ share your own `buyAmount` takes, the fees and the migration terms) and `devBuyMax`, the dev-buy
63
+ ceiling that applies. `exp-custom` works only where the deployment has switched it on. See
62
64
  [Custom curves](https://docs.candle.tv/developers/headless-launch#custom-curves-choose-v-and-g-switched-off).
63
65
 
64
66
  For a full walkthrough of `candle_launch_and_seed` and `candle_trade`, including the keyless read
package/dist/index.js CHANGED
@@ -151,7 +151,7 @@ import { lstat, open, realpath } from "node:fs/promises";
151
151
  import { dirname, isAbsolute, relative, sep } from "node:path";
152
152
 
153
153
  // src/version.ts
154
- var SERVER_VERSION = "0.10.20";
154
+ var SERVER_VERSION = "0.10.21";
155
155
 
156
156
  // src/update-notice.ts
157
157
  var PLAIN_VERSION = /^\d+\.\d+\.\d+$/;
@@ -2474,18 +2474,55 @@ function perpsCandleWindow(opts, now) {
2474
2474
  return usage(`interval must be one of ${PERPS_CANDLE_INTERVALS.join(" ")}.`);
2475
2475
  const count = bounded(opts.count, 100, 1, 5000, "count");
2476
2476
  const raw = opts.end;
2477
- const endTime = raw === undefined ? now : typeof raw === "number" || /^\d+$/.test(raw) ? Number(raw) : /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/.test(raw) ? Date.parse(raw) : NaN;
2477
+ const endTime = raw === undefined ? now : epoch(raw);
2478
2478
  const startTime = endTime - count * at(INTERVAL_MS, index);
2479
2479
  if (!Number.isSafeInteger(endTime) || endTime < 0 || endTime > now || startTime < 0)
2480
2480
  return usage("end must be an ISO 8601 time or epoch milliseconds, no later than now.");
2481
2481
  return { interval, count, startTime, endTime };
2482
2482
  }
2483
+ function epoch(raw) {
2484
+ return typeof raw === "number" || /^\d+$/.test(raw) ? Number(raw) : /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/.test(raw) ? Date.parse(raw) : NaN;
2485
+ }
2486
+ var PERPS_FUNDING_HISTORY_MAX_MS = 30 * 86400000;
2487
+ var PERPS_FUNDING_HISTORY_MAX_ROWS = 500;
2488
+ function perpsFundingWindow(opts, now) {
2489
+ const endTime = opts.end === undefined ? now : epoch(opts.end);
2490
+ if (!Number.isSafeInteger(endTime) || endTime < 0 || endTime > now)
2491
+ return usage("end must be an ISO 8601 time or epoch milliseconds, no later than now.");
2492
+ const startTime = opts.start === undefined ? endTime - 86400000 : epoch(opts.start);
2493
+ if (!Number.isSafeInteger(startTime) || startTime < 0 || startTime > endTime)
2494
+ return usage("start must be an ISO 8601 time or epoch milliseconds, no later than end.");
2495
+ if (endTime - startTime > PERPS_FUNDING_HISTORY_MAX_MS)
2496
+ return usage("start must be at most 30 days before end.");
2497
+ return { startTime, endTime };
2498
+ }
2483
2499
  function bounded(value, fallback, min, max, name) {
2484
2500
  const n = value ?? fallback;
2485
2501
  if (!Number.isInteger(n) || n < min || n > max)
2486
2502
  return usage(`${name} must be an integer from ${min} to ${max}.`);
2487
2503
  return n;
2488
2504
  }
2505
+ function parsePerpsFundingHistory(value, coin, window) {
2506
+ if (!Array.isArray(value))
2507
+ return unavailable();
2508
+ const funding = [];
2509
+ for (const row of value) {
2510
+ if (!object(row) || row.coin !== coin || !integer(row.time) || !decimal2(row.fundingRate) || !decimal2(row.premium))
2511
+ return unavailable();
2512
+ if (row.time >= window.startTime && row.time <= window.endTime)
2513
+ funding.push({
2514
+ time: row.time,
2515
+ fundingRate: row.fundingRate,
2516
+ fundingAprPct: fixed(scaled(row.fundingRate) * 876000n, 2),
2517
+ premium: row.premium
2518
+ });
2519
+ }
2520
+ funding.sort((a, b) => a.time - b.time);
2521
+ return {
2522
+ funding: funding.slice(0, PERPS_FUNDING_HISTORY_MAX_ROWS),
2523
+ truncated: funding.length > PERPS_FUNDING_HISTORY_MAX_ROWS
2524
+ };
2525
+ }
2489
2526
 
2490
2527
  class PerpsMarketReader {
2491
2528
  opts;
@@ -2763,6 +2800,44 @@ class PerpsMarketReader {
2763
2800
  candles: candles.slice(-window.count)
2764
2801
  };
2765
2802
  }
2803
+ async fundingHistory(coin, opts = {}) {
2804
+ this.key();
2805
+ const window = perpsFundingWindow(opts, this.now());
2806
+ const [config, core] = await Promise.all([this.config(), this.core()]);
2807
+ const canonical2 = at(core.universe, resolve(core, coin)).name;
2808
+ const value = await info(this.opts.fetcher, this.opts.network, {
2809
+ type: "fundingHistory",
2810
+ coin: canonical2,
2811
+ startTime: window.startTime,
2812
+ endTime: window.endTime
2813
+ });
2814
+ return {
2815
+ ...this.envelope(config),
2816
+ coin: canonical2,
2817
+ startTime: window.startTime,
2818
+ endTime: window.endTime,
2819
+ ...parsePerpsFundingHistory(value, canonical2, window)
2820
+ };
2821
+ }
2822
+ async predictedFunding() {
2823
+ this.key();
2824
+ const [config, core, extras] = await Promise.all([this.config(), this.core(), this.extras()]);
2825
+ if (extras.predicted === null)
2826
+ return unavailable();
2827
+ const predicted = [];
2828
+ for (const meta of core.universe) {
2829
+ const row = extras.predicted.get(meta.name);
2830
+ if (row)
2831
+ predicted.push({
2832
+ coin: meta.name,
2833
+ fundingRate: row.fundingRate,
2834
+ fundingAprPct: fixed(scaled(row.fundingRate) * 876000n, 2),
2835
+ nextFundingTime: row.nextFundingTime
2836
+ });
2837
+ }
2838
+ predicted.sort((a, b) => a.coin < b.coin ? -1 : a.coin > b.coin ? 1 : 0);
2839
+ return { ...this.envelope(config, extras.warnings, this.extrasCache?.at ?? this.now()), predicted };
2840
+ }
2766
2841
  }
2767
2842
 
2768
2843
  // src/perps-history.ts
@@ -4570,7 +4645,7 @@ function planTableMarkdown(table) {
4570
4645
 
4571
4646
  // src/tools.ts
4572
4647
  var VAMP_CANDIDATES_DESCRIPTION = "Lists Solana tokens that are close to graduating or graduated in the last 6 hours whose ticker other tokens " + "are copying, most-copied in the last 24 hours first. Each entry carries a launch draft (name, ticker, image URL) " + "for `candle_launch_token` or `candle_launch_and_seed`; draft notes identify fields you must supply or adjust. " + "Upload your own image through the agent image upload when the image is unavailable. Use `dryRun: true` first. " + "Counts exclude the candidate itself and cover both Solana and Hood launches at the venues returned in " + "`coverage`, not Candle's own. `coverage.since` is the returned Solana coverage date; Hood history may differ.";
4573
- var CUSTOM_CURVE_RULE = 'mode "exp-custom" with curve { virtualQuote, threshold } launches a Solana curve with your own V (the virtual ' + "quote it starts with) and G (the quote it raises before graduating), in whole SOL or CNDL as decimal strings, " + "accepted when V is 1 to 100 SOL and G 1 to 250 SOL (66,000 to 6,600,000 and 66,000 to 16,500,000 CNDL), when " + "every price up to 20 x G stays under the ceiling (V + 20G)^2 < V x X at this stakerAllocationBps, when neither " + "is finer than the quote's decimals, and when the dev buy is at most 10% of G; a curve past a limit is " + "CURVE_OUT_OF_RANGE, and at the ceiling it carries maxThresholdRaw, the largest G that fits. " + "Dry-run it first (dryRun: true): the dry run spends nothing and returns curveTerms, the market cap at start and " + "at graduation and the supply sold by graduation. Only where the API has EXP_CUSTOM_CURVES on; " + "elsewhere exp-custom is refused as an unknown mode.";
4648
+ var CUSTOM_CURVE_RULE = 'mode "exp-custom" with curve { virtualQuote, threshold } launches a Solana curve with your own V (the virtual ' + "quote it starts with) and G (the quote it raises before graduating), in whole SOL or CNDL as decimal strings, " + "accepted when V is 1 to 100 SOL and G 1 to 250 SOL (66,000 to 6,600,000 and 66,000 to 16,500,000 CNDL), when " + "every price up to 20 x G stays under the ceiling (V + 20G)^2 < V x X at this stakerAllocationBps, when neither " + "is finer than the quote's decimals, and when the dev buy is at most 0.5 SOL (2,000,000 CNDL), or 10% of G when " + "that is lower (every exp- mode; an operator may set the 0.5 SOL lower); a curve past a limit is " + "CURVE_OUT_OF_RANGE, and at the ceiling it carries maxThresholdRaw, the largest G that fits. " + "Dry-run it first (dryRun: true): the dry run spends nothing and returns curveTerms, the market cap at start and " + "at graduation, the supply sold by graduation, the share your own buyAmount takes, the fees and the migration " + "terms, and devBuyMax, the dev-buy ceiling that applies. Only where the API has EXP_CUSTOM_CURVES on; " + "elsewhere exp-custom is refused as an unknown mode.";
4574
4649
  var localProfileTools = new WeakMap;
4575
4650
  var STAKING_DISCLOSURE = "No instant unstake. Read pool.withdrawalTimelockSeconds for the wait. Solana rewards accrue to the holding wallet. sCNDL in any eligible account wallet counts toward Pro and earns the account's Hood staking reward, except a wallet another account also claims, wallets past the 200-wallet cap, and an account with no EVM wallet. CNDL in a pending withdrawal counts toward nothing for Pro.";
4576
4651
  var stakingWriteShape = {
@@ -5003,12 +5078,23 @@ var launchTokenShape = {
5003
5078
  bannerUrl: z4.string().optional().describe("Optional https URL to WIDE artwork for the token page's banner strip (wider than 1.5:1, " + "e.g. 1200x630). This is where a share card or OG image belongs. A square image here is " + "rejected with BANNER_WRONG_SHAPE: send the wide original, not a cropped copy. Omit it and the strip " + "falls back to imageUrl"),
5004
5079
  chain: z4.string().optional().describe('"solana" or "hood"; defaults to solana'),
5005
5080
  quoteAsset: z4.string().optional().describe("Quote asset symbol; defaults per chain"),
5006
- mode: z4.string().optional().describe('"open" or "exclusive"; defaults to open. "test-open" / "test-exclusive" launch the ' + "low-threshold test curves (~1/80 economics) where the API has ENABLE_TEST_CURVES on. " + '"exp-custom" launches a Solana curve with your own V and G, given in curve, where the API has it ' + "switched on"),
5081
+ mode: z4.string().optional().describe('"open" or "exclusive"; defaults to open. "test-open" / "test-exclusive" launch the ' + "low-threshold test curves (~1/80 economics) where the API has ENABLE_TEST_CURVES on. " + '"exp-a", "exp-b" and "exp-c" are the experimental fast Solana curves (sol or cndl). ' + '"exp-custom" launches a Solana curve with your own V and G, given in curve, where the API has it ' + "switched on"),
5007
5082
  curve: z4.object({
5008
5083
  virtualQuote: z4.string().describe('V: the virtual quote the curve starts with, in whole quote units, e.g. "12" SOL'),
5009
5084
  threshold: z4.string().describe('G: the quote the curve raises before it graduates, in whole quote units, e.g. "40" SOL')
5010
5085
  }).strict().optional().describe('mode "exp-custom" only, and required there. Decimal strings in whole units of the quote asset (sol or ' + "cndl), never raw units. Dry-run first: the API checks the limits and returns the curve's terms"),
5086
+ creatorFeeBps: z4.union([z4.literal(0), z4.literal(100), z4.literal(200)]).optional().describe("An exp- mode only: an extra creator fee of 100 or 200 basis points on top of the curve's 1% and the " + "pool's 0.5%, paid to the creator in full. 0 is the same as leaving it out"),
5011
5087
  stakerAllocationBps: z4.number().optional().describe("Staker allocation in basis points"),
5088
+ poolFee: z4.union([
5089
+ z4.object({ tier: z4.union([z4.literal(25), z4.literal(50), z4.literal(100)]) }).strict(),
5090
+ z4.object({
5091
+ startBps: z4.number().int().max(2500),
5092
+ endBps: z4.union([z4.literal(25), z4.literal(50), z4.literal(100)]),
5093
+ minutes: z4.number().positive().max(30)
5094
+ }).strict()
5095
+ ]).optional().describe("Solana only: the graduated pool's fee. { tier: 25 | 50 | 100 } is a flat fee in bps (default 50), or " + "{ startBps, endBps, minutes } is an anti-snipe fall from startBps (at most 2500, at least endBps) to " + "endBps (25, 50 or 100) over minutes (at most 30). Cannot be combined with creatorFeeBps unless the tier is 50. " + "Omit for the standard 50 bps pool"),
5096
+ poolOpen: z4.enum(["legacy", "curve_price"]).optional().describe(`Solana preset tiers only. "curve_price" opens the pool at the curve's last price (the whole reserve is ` + "seeded and the remaining base burned); stakerAllocationBps plus the team allocation must fit the tier's limit, " + "which the dry run returns as graduation.maxStakerCarveBps. Omit for legacy"),
5097
+ expPoolShape: z4.enum(["floor", "full_range"]).optional().describe('Solana exp- curves only. "full_range" opens the pool across the whole price range. Omit for floor'),
5012
5098
  dexVersion: z4.string().nullable().optional().describe('"v3" or "v4"; required for hood launches'),
5013
5099
  buyAmount: z4.union([z4.string(), z4.number()]).optional().describe("Initial buy, in quote base units (string or number)"),
5014
5100
  description: z4.string().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@candledottv/mcp",
3
- "version": "0.10.20",
3
+ "version": "0.10.21",
4
4
  "description": "MCP server for the Candle agent rail: launch tokens, read markets, report activity",
5
5
  "type": "module",
6
6
  "license": "MIT",