@candledottv/mcp 0.10.6 → 0.10.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +47 -37
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -106,7 +106,7 @@ function percentOfBalance(balanceRaw, percent) {
106
106
  import { randomUUID } from "node:crypto";
107
107
 
108
108
  // src/version.ts
109
- var SERVER_VERSION = "0.10.6";
109
+ var SERVER_VERSION = "0.10.8";
110
110
 
111
111
  // src/update-notice.ts
112
112
  var PLAIN_VERSION = /^\d+\.\d+\.\d+$/;
@@ -1351,7 +1351,7 @@ async function executePerpsDeposit(args, cfg, env, fetch2) {
1351
1351
  }
1352
1352
 
1353
1353
  // src/plans.ts
1354
- var PLAN_CAPABILITY_NOTE = "A capability marked yes is what the plan allows. It is subject to the deployment's own switches (perps and own-wallet bridges each have one) and to the wallet, scopes and setup the feature needs.";
1354
+ var PLAN_CAPABILITY_NOTE = "A capability marked yes is what the plan allows. It is subject to the deployment's own switches (perps and own-wallet bridges each have one) and to the wallet, scopes and setup the feature needs. Every plan can launch a token from its embedded wallet, with an optional dev buy (same transaction on Solana; best-effort follow-up on Hood), through the headless launch; the two launch rows are additional routes, not the only ones.";
1355
1355
  var PLAN_CAPABILITY_LABELS = {
1356
1356
  tradeCandleTokens: "Trade Candle-launched tokens",
1357
1357
  tradeBaseAssets: "Trade base assets",
@@ -1359,8 +1359,8 @@ var PLAN_CAPABILITY_LABELS = {
1359
1359
  sellExternalTokens: "Sell tokens not launched on Candle",
1360
1360
  buyExternalTokens: "Buy tokens not launched on Candle",
1361
1361
  hyperliquidPerps: "Hyperliquid perps (when enabled)",
1362
- selfLaunch: "Self-launch from a linked wallet",
1363
- atomicLaunch: "Atomic launch with first buys",
1362
+ selfLaunch: "Launch from a linked or TEE wallet (self-signed)",
1363
+ atomicLaunch: "Atomic launch: launch + 1–4 first buys in one bundle",
1364
1364
  createLinkedWallets: "Create linked wallets",
1365
1365
  importLinkedWallets: "Import linked wallets",
1366
1366
  limitOrders: "Limit orders",
@@ -1641,9 +1641,9 @@ ${promotion}` : markdown;
1641
1641
  return "";
1642
1642
  }
1643
1643
  }
1644
- async function callAndRelay(name, args, cfg) {
1644
+ async function callAndRelay(name, args, cfg, doFetch) {
1645
1645
  const { url, init } = buildRequest(name, args, cfg);
1646
- const res = await fetch(url, init);
1646
+ const res = await doFetch(url, init);
1647
1647
  noteVersionHeaders(res);
1648
1648
  const text = await res.text();
1649
1649
  return {
@@ -1734,7 +1734,7 @@ var { buyAmount: _rawBuyAmount, ...seedableLaunchShape } = launchTokenShape;
1734
1734
  var launchAndSeedShape = {
1735
1735
  ...seedableLaunchShape,
1736
1736
  clientLaunchId: z2.string().optional().describe("Idempotency key. Auto-generated when omitted and echoed in the result."),
1737
- devBuy: z2.string().optional().describe('Seed buy in DECIMAL units of the quote asset this launch selects (e.g. "0.25" SOL, or ' + "ETH on hood), bundled into the launch transaction itself. Follows quoteAsset, which " + "defaults to sol on solana and eth on hood. Capped by the platform dev-buy ceiling; for " + "a larger seed, launch then follow with candle_trade.")
1737
+ devBuy: z2.string().optional().describe('Seed buy in DECIMAL units of the quote asset this launch selects (e.g. "0.25" SOL, or ' + "ETH on hood), bundled into the launch transaction itself on solana and sent as a follow-up " + "transaction on hood. Paid from the account's embedded wallet. Follows quoteAsset, which " + "defaults to sol on solana and eth on hood. Capped by the platform dev-buy ceiling; for " + "a larger seed, launch then follow with candle_trade.")
1738
1738
  };
1739
1739
  var transferShape = {
1740
1740
  chain: z2.enum(["solana", "hood"]).describe("Which chain the transfer executes on"),
@@ -1752,28 +1752,38 @@ var sweepShape = {
1752
1752
  function registerTools(server, env = process.env) {
1753
1753
  const cfg = resolveConfig(env);
1754
1754
  const allowed = resolveToolAllowlist(env);
1755
- const register = (name, ...rest) => {
1755
+ registerToolSubset(server, { tools: [...allowed], getConfig: () => cfg, env });
1756
+ }
1757
+ function registerToolSubset(server, options) {
1758
+ const unknown = options.tools.filter((name) => !TOOL_NAMES.includes(name));
1759
+ if (unknown.length)
1760
+ throw new Error(`Unknown tool name(s): ${unknown.join(", ")}`);
1761
+ const allowed = new Set(options.tools);
1762
+ const getConfig = options.getConfig;
1763
+ const env = options.env ?? {};
1764
+ const doFetch = options.fetch ?? ((url, init) => fetch(url, init));
1765
+ const register = (name, definition, handler) => {
1756
1766
  if (!allowed.has(name))
1757
1767
  return;
1758
- return server.registerTool(name, ...rest);
1768
+ server.registerTool(name, { ...definition, ...options.metadata?.[name] }, handler);
1759
1769
  };
1760
1770
  register("candle_launch_token", {
1761
1771
  title: "Launch a token on Candle",
1762
- description: "Launch a new token via the Candle headless launch API. Set dryRun: true to validate without spending anything.",
1772
+ description: "Launch a new token via the Candle headless launch API, from the account's embedded wallet. Works on every plan, Free included. Set dryRun: true to validate without spending anything.",
1763
1773
  inputSchema: launchTokenShape
1764
- }, async (args) => callAndRelay("candle_launch_token", args, cfg));
1774
+ }, async (args) => callAndRelay("candle_launch_token", args, getConfig(), doFetch));
1765
1775
  register("candle_get_market", {
1766
1776
  title: "Get market state",
1767
1777
  description: "Read Candle and indexed external markets, including indexed-but-not-routable tokens. No key needed. " + "Read candleLaunched, launchpad, venue and trade.routable; jupiterOk and discovery flags are distinct. " + "Routability is stored eligibility, not a quote or permission. General quotes use POST /api/v1/trade/agent/quote. " + "Curve quotes and lifecycle describe Candle launches. MARKET_NOT_FOUND is a legacy code: read " + "error.routing.reason, error.discovery and sibling error.retryable. A curve-only 404 does not mean untradeable.",
1768
1778
  inputSchema: getMarketShape
1769
- }, async (args) => callAndRelay("candle_get_market", args, cfg));
1779
+ }, async (args) => callAndRelay("candle_get_market", args, getConfig(), doFetch));
1770
1780
  register("candle_token_forensics", {
1771
1781
  title: "Token forensics",
1772
1782
  description: `Gate a buy before making it: who launched it (resolved on-chain; pump.fun's shared updateAuthority is never the developer; when no developer is on chain, deployer.attribution names the launchpad or issuer instead, e.g. launched via stonk.fun or issued by xStocks), their went-to-zero rate and last coins, who bought in the deploy window (the creator's own wallets are marked disclosed; strangers in the same slot are the bundle signal), same-funder insider share, same-funder deployer cluster, and safety.summary with six sourced flags (mintAuthority, freezeAuthority, tokenExtensions, lpLock, sellability, liquidityDrain). Refuse an unprompted buy when flagged; incomplete or unknown is not clearance. launch.deployerLaunches is an inclusive informational count, never a warning. Every measurement carries a coverage note -- 'unavailable' is not 'clean'. No key needed.
1773
1783
 
1774
1784
  MARKET_NOT_FOUND means Candle has no market for that token and this could not run. That is also not 'clean': report that you could not check it, rather than reporting the token as safe. That refusal now carries error.coverage -- covered:false, a reason ('external_launchpad' when the token launched somewhere else, 'unknown_mint' when nobody has indexed it), the launchpad when known, and every check that consequently did not run. Read it instead of guessing. Most of the feed, and any Solana mint Jupiter's index knows, now answers with a partial report instead. On Hood, a token Candle did not launch names its launch account (deployer.method cvc_launch_account) for pons.family and pools.trade launches, with no record of earlier coins; other Hood launchpads return no developer.`,
1775
1785
  inputSchema: tokenForensicsShape
1776
- }, async (args) => callAndRelay("candle_token_forensics", args, cfg));
1786
+ }, async (args) => callAndRelay("candle_token_forensics", args, getConfig(), doFetch));
1777
1787
  register("candle_get_feed", {
1778
1788
  title: "Get a token feed",
1779
1789
  description: "Read one of the trade page's public feeds: new, graduated, onfire, or bluechip. Reads " + `only; moves nothing. No key needed. Start here when nobody has named a token.
@@ -1782,24 +1792,24 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
1782
1792
 
1783
1793
  ` + "Filter, sort and pick fields SERVER-SIDE rather than reading the whole feed: an " + "unfiltered response is around 135KB and will not fit in a tool result. See `where`, " + "`sort` and `fields`.\n\n" + "One rule to know before screening on safety: a missing field is NOT a false one. " + "mintAuthorityDisabled and freezeAuthorityDisabled are absent on a real share of rows, " + "and absent means nobody checked, not that the authority is disabled. `where` never lets " + 'an absent field satisfy a comparison, so {"mintAuthorityDisabled":{"eq":true}} returns ' + "only tokens that actually say so.",
1784
1794
  inputSchema: getFeedShape
1785
- }, async (args) => callAndRelay("candle_get_feed", args, cfg));
1795
+ }, async (args) => callAndRelay("candle_get_feed", args, getConfig(), doFetch));
1786
1796
  register("candle_report_activity", {
1787
1797
  title: "Report on-chain activity",
1788
1798
  description: "Report a client-executed transaction (transfer, swap, stake) so Candle records and verifies it.",
1789
1799
  inputSchema: reportActivityShape
1790
- }, async (args) => callAndRelay("candle_report_activity", args, cfg));
1800
+ }, async (args) => callAndRelay("candle_report_activity", args, getConfig(), doFetch));
1791
1801
  register("candle_get_agent_profile", {
1792
1802
  title: "Get an agent profile",
1793
1803
  description: "Read a Candle user's public agent profile: whether agent features are enabled and launch counts.",
1794
1804
  inputSchema: getAgentProfileShape
1795
- }, async (args) => callAndRelay("candle_get_agent_profile", args, cfg));
1805
+ }, async (args) => callAndRelay("candle_get_agent_profile", args, getConfig(), doFetch));
1796
1806
  register("candle_get_plans", {
1797
1807
  title: "Plans: prices, fees, limits and what each can do",
1798
1808
  description: "The plan table this Candle deployment serves (GET /api/v1/agent/plans). No key needed. Reads only. For " + "each plan: price (null when not sold), feeBps (the agent fee charged on top of every trade or dev buy the " + "API builds), perpFeeBps (the Hyperliquid builder fee), limits for a new key (requests per minute, launches " + "per day, uploads per minute, linked wallets) and capabilities: buyExternalTokens, sellExternalTokens, " + "tradeBaseAssets, tradeCandleTokens, selfLaunch, atomicLaunch, createLinkedWallets, importLinkedWallets, " + "hyperliquidPerps, limitOrders, quant, freeBaseTransfers. promoMaxDays is the days of Max a first Pro " + "purchase includes (0: no promotion). Quote prices and fees from here, never from memory: they differ by " + "deployment and change at the three-plan launch (Free, Pro, Max). The account's own plan and fee are in " + "candle_execution_status's tier. A TIER_REQUIRED refusal names a capability this table shows the account's " + "plan lacks. A capability true here is plan eligibility, not deployment availability: perps and own-wallet bridges " + "are each behind a deployment switch and need their wallet, scopes and setup. Returns the server's JSON, then the same table as Markdown.",
1799
1809
  inputSchema: {}
1800
1810
  }, async () => {
1801
- const { url, init } = buildRequest("candle_get_plans", {}, cfg);
1802
- const res = await fetch(url, init);
1811
+ const { url, init } = buildRequest("candle_get_plans", {}, getConfig());
1812
+ const res = await doFetch(url, init);
1803
1813
  noteVersionHeaders(res);
1804
1814
  const text = await res.text();
1805
1815
  if (!res.ok)
@@ -1823,43 +1833,43 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
1823
1833
 
1824
1834
  ` + "Amounts come back RAW, deliberately: the answer you need after a timeout is the outcome, " + "and you already know what you asked for.",
1825
1835
  inputSchema: getOperationShape
1826
- }, async (args) => callAndRelay("candle_get_operation", args, cfg));
1836
+ }, async (args) => callAndRelay("candle_get_operation", args, getConfig(), doFetch));
1827
1837
  register("candle_get_wallets", {
1828
1838
  title: "List the wallets Candle executes with",
1829
1839
  description: "The account's EMBEDDED wallets, one per chain, with their delegation state. These are " + "the wallets candle_trade, candle_swap and candle_transfer spend from, so this is how an " + "agent finds its own funding addresses. Reads only; moves nothing. Not the same as the " + "account's LINKED wallets, which are the owner's own wallets and are not spent from here. " + "Balances are not included: read a specific one with the market and balance endpoints.",
1830
1840
  inputSchema: {}
1831
- }, async () => callAndRelay("candle_get_wallets", {}, cfg));
1841
+ }, async () => callAndRelay("candle_get_wallets", {}, getConfig(), doFetch));
1832
1842
  register("candle_get_profile_wallets", {
1833
1843
  title: "Read which wallets an agent profile can spend from",
1834
1844
  description: "An agent profile (API key) either spends from EVERY wallet on its account or only from " + "the ones assigned to it. Read walletScope before drawing any conclusion from the list: " + "an empty list means 'every wallet' under scope 'all' and 'none at all' under 'selected'. " + "Reads only; moves nothing.",
1835
1845
  inputSchema: profileWalletsShape
1836
- }, async (args) => callAndRelay("candle_get_profile_wallets", args, cfg));
1846
+ }, async (args) => callAndRelay("candle_get_profile_wallets", args, getConfig(), doFetch));
1837
1847
  register("candle_set_profile_wallets", {
1838
1848
  title: "Set which wallets an agent profile can spend from",
1839
1849
  description: "REPLACES the profile's whole wallet set with the ids given, so a wallet left out of the " + "list loses access; pass an empty list to leave the profile with no wallets. NARROWING " + "ONLY: an API key can remove wallets from its OWN profile, but naming one it does not " + "already hold is a grant and needs a human in the dashboard, as does editing any other " + "profile. Takes effect only while the profile's scope is 'selected'. Wallet ids are the " + "linked-wallet ids, not addresses.",
1840
1850
  inputSchema: setProfileWalletsShape
1841
- }, async (args) => callAndRelay("candle_set_profile_wallets", args, cfg));
1851
+ }, async (args) => callAndRelay("candle_set_profile_wallets", args, getConfig(), doFetch));
1842
1852
  register("candle_get_profile_pnl", {
1843
1853
  title: "Read an agent profile's P&L",
1844
- description: "This profile's share of the account's P&L: realized profit on the sales it made, the Candle fees " + "charged against it, and the positions that belong to it (the wallet's bound key, else the key " + "whose buy opened them) with their cost basis, each MARKED at Candle's current price where one " + "exists: `markPriceUsd`, `marketValueUsd` and `unrealizedUsd` per position, and `unrealizedUsd` " + "overall. A position with no price is counted in `unmarkedPositions` and left out of unrealized, " + "never valued at zero; `oldestMarkAt` says how old the marks are. Deposits and withdrawals are " + "excluded: funding a wallet is not profit. Check `unvalued` and `truncated` before quoting the " + "number; they mean the total is partial. The figures come from the account's one P&L read, the " + "same as the console's, to the cent: `pnl.totalUsd` (realized net plus unrealized) is the " + "console's P&L. Positions live in wallets: each wallet is its own average-cost pool, the total " + "is the sum of `pnl.byWallet` (one row per wallet, main or linked, with the linked wallet's " + "label; `unknown:solana` or `unknown:hood` is the row for fills no record places in a wallet), " + "and one token held in two wallets is two positions, each with its `wallet`. A recorded move " + "between the account's wallets carries its cost and realizes nothing. Each position carries " + "`agent` (the key it belongs to) and `dust` when it is worth under one cent; dust stays listed " + "and counted, and `openPositionsExDust` leaves it out. `closed` gives the closed positions as " + "`madeUsd` + `lostUsd` + `partialSellsUsd` = `realizedNetUsd`, with `wins` and `losses`. " + "`lookback` and `truncated` are the account's activity bound, and `tradesConsidered` counts this " + "key's ledger fills. Since 2026-10-02 the total is the sum of wallets rather than one pool across " + "them, so realized figures can differ from earlier reads; a server that predates it omits " + "`totalUsd`, `closed`, `openPositionsExDust`, `wallet`, `agent` and `dust`. " + "Every open position and every `byWallet` row (and its positions) carries `chain` ('solana' or " + "'hood'), and `pnl.byChain` has the total's figures over each chain's fills alone, both chains " + "always present, with `openPositions` there a count (the positions are in `pnl.openPositions`, by " + "`chain`). The two chains sum to the total. A " + "server that predates per-chain P&L omits `chain` and `byChain`. While Hyperliquid is enabled, " + "`hyperliquid` separately reports main-perp realized gross, signed funding, fees (inclusive of builder fees), " + "and net = gross + funding - fees, for currently bound EVM TEE wallets since the current TEE binding. " + "Check `read` and `truncated`; historical bindings and unrealized perps are excluded. Reads only.",
1854
+ description: "This profile's share of the account's P&L: realized profit on the sales it made, the Candle fees " + "charged against it, and the positions that belong to it (the wallet's bound key, else the key " + "whose buy opened them) with their cost basis, each MARKED at Candle's current price where one " + "exists: `markPriceUsd`, `marketValueUsd` and `unrealizedUsd` per position, and `unrealizedUsd` " + "overall. A position with no price is counted in `unmarkedPositions` and left out of unrealized, " + "never valued at zero; `oldestMarkAt` says how old the marks are. Deposits and withdrawals are " + "excluded: funding a wallet is not profit. Check `unvalued` and `truncated` before quoting the " + "number; they mean the total is partial. The figures come from the account's one P&L read, the " + "same as the console's, to the cent: `pnl.totalUsd` (realized net plus unrealized) is the " + "console's P&L. Positions live in wallets: each wallet is its own average-cost pool, the total " + "is the sum of `pnl.byWallet` (one row per wallet, main or linked, with the linked wallet's " + "label; `unknown:solana` or `unknown:hood` is the row for fills no record places in a wallet), " + "and one token held in two wallets is two positions, each with its `wallet`. A recorded move " + "between the account's wallets carries its cost and realizes nothing. Moved-in positions and " + "`closedPositions` carry additive `transferredIn`, `basisSource` and `basisComplete`; " + "`basisSource` says whether cost comes from Candle history, on-chain trades or the arrival price; " + "`zero-cost` (with `zeroCostQuantity`) is Solana CNDL with no purchase found, carried at $0. " + "Public `realizedFromTransfersUsd` and `unrealizedFromTransfersUsd` are shares already included in " + "total P&L and rank; do not add them again. Moved-in closes count toward W/L and eligibility. Each position carries " + "`agent` (the key it belongs to) and `dust` when it is worth under one cent; dust stays listed " + "and counted, and `openPositionsExDust` leaves it out. `closed` gives the closed positions as " + "`madeUsd` + `lostUsd` + `partialSellsUsd` = `realizedNetUsd`, with `wins` and `losses`. " + "`lookback` and `truncated` are the account's activity bound, and `tradesConsidered` counts this " + "key's ledger fills. Since 2026-10-02 the total is the sum of wallets rather than one pool across " + "them, so realized figures can differ from earlier reads; a server that predates it omits " + "`totalUsd`, `closed`, `openPositionsExDust`, `wallet`, `agent` and `dust`. " + "Every open position and every `byWallet` row (and its positions) carries `chain` ('solana' or " + "'hood'), and `pnl.byChain` has the total's figures over each chain's fills alone, both chains " + "always present, with `openPositions` there a count (the positions are in `pnl.openPositions`, by " + "`chain`). The two chains sum to the total. A " + "server that predates per-chain P&L omits `chain` and `byChain`. While Hyperliquid is enabled, " + "`hyperliquid` separately reports main-perp realized gross, signed funding, fees (inclusive of builder fees), " + "and net = gross + funding - fees, for currently bound EVM TEE wallets since the current TEE binding. " + "Check `read` and `truncated`; historical bindings and unrealized perps are excluded. Reads only.",
1845
1855
  inputSchema: profilePnlShape
1846
- }, async (args) => callAndRelay("candle_get_profile_pnl", args, cfg));
1856
+ }, async (args) => callAndRelay("candle_get_profile_pnl", args, getConfig(), doFetch));
1847
1857
  register("candle_get_profile_trades", {
1848
1858
  title: "Read an agent profile's trade history",
1849
1859
  description: "Orders, actual fills, fees, timestamps and transaction hashes for this profile. Includes FAILED " + "trades, with an errorCode saying why each did not go through, so this answers 'what happened to " + "my order' as well as 'what did I trade'. Reads only; moves nothing.",
1850
1860
  inputSchema: profileTradesShape
1851
- }, async (args) => callAndRelay("candle_get_profile_trades", args, cfg));
1861
+ }, async (args) => callAndRelay("candle_get_profile_trades", args, getConfig(), doFetch));
1852
1862
  register("candle_get_portfolio", {
1853
1863
  title: "Read what the account holds, on Solana and Hood",
1854
- description: "Balances and prices for the wallets Candle already knows on this account, on both chains: the " + "embedded wallet and every TEE wallet. Solana wallets are in `embedded` and `tee` (SOL as raw " + "`lamports`, tokens as raw `amountRaw` with `decimals`). Hood wallets are in `hood.embedded` and " + "`hood.tee` (ETH as raw `wei`, ERC-20s including USDG), never in the top-level arrays. Every " + "wallet and holding carries `chain`. Prices are in `prices`: a Solana mint under its own " + "address, Hood ETH under `hood:native`, and a Hood token under `hood:<contract lowercased>`, so " + "lowercase the address before looking it up. An unpriced entry is `priceUsd: null`, never zero; " + "a Hood one says why in `unpricedReason` (no-market-row, unusable-price, stale-mark, " + "source-unavailable), and `hood.unpriced` / `hood.unpricedByReason` count them. A Hood token " + "mark older than six hours is not used, so many Hood tokens read unpriced. A wallet whose read " + "failed has null balances, never zero, and is listed in `unavailable` (Solana) or " + "`hood.unavailable`; check `complete` before quoting a total. Vault and external wallets are " + "not included: Candle does not know their addresses (the Candle CLI's `candle portfolio` reads " + "them over your own RPC). Needs a key with the account:read scope; without it the answer is " + "SCOPE_MISSING. A server that predates Hood in the portfolio omits `hood` and `chain`. Reads " + "only; moves nothing.",
1864
+ description: "Balances and prices for the wallets Candle already knows on this account, on both chains: the " + "embedded wallet and every TEE wallet. Solana wallets are in `embedded` and `tee` (SOL as raw " + "`lamports`, tokens as raw `amountRaw` with `decimals`). Hood wallets are in `hood.embedded` and " + "`hood.tee` (ETH as raw `wei`, ERC-20s including USDG), never in the top-level arrays. Every " + "wallet and holding carries `chain`. Prices are in `prices`: a Solana mint under its own " + "address, Hood ETH under `hood:native`, and a Hood token under `hood:<contract lowercased>`, so " + "lowercase the address before looking it up. An unpriced entry is `priceUsd: null`, never zero; " + "a Hood one says why in `unpricedReason` (no-market-row, unusable-price, stale-mark, " + "source-unavailable), and `hood.unpriced` / `hood.unpricedByReason` count them. A Hood token " + "mark older than six hours is not used, so many Hood tokens read unpriced. A wallet whose read " + "failed has null balances, never zero, and is listed in `unavailable` (Solana) or " + "`hood.unavailable`; check `complete` before quoting a total. `walletsComplete` is false only " + "when the wallet list itself was cut off, and is absent on a server that predates it: then " + "`complete: false` may be a cut-off list or a failed read. Vault and external wallets are " + "not included: Candle does not know their addresses (the Candle CLI's `candle portfolio` reads " + "them over your own RPC). Needs a key with the account:read scope; without it the answer is " + "SCOPE_MISSING. A server that predates Hood in the portfolio omits `hood` and `chain`. Reads " + "only; moves nothing.",
1855
1865
  inputSchema: {}
1856
- }, async () => callAndRelay("candle_get_portfolio", {}, cfg));
1866
+ }, async () => callAndRelay("candle_get_portfolio", {}, getConfig(), doFetch));
1857
1867
  register("candle_resolve_token", {
1858
1868
  title: "Resolve a contract address to a token",
1859
1869
  description: "Turn a bare contract address or mint into Candle's market for it: chain, symbol, " + "decimals, quote asset, and whether Candle can trade it. Start here when a human gives " + "you an address and nothing else. The chain is read off the address's own shape and is " + "not guessed, so it does not need to be supplied. Reads only; moves nothing. A 404 means " + "Candle has no market for that address, which is an answer, not a failure to retry.",
1860
1870
  inputSchema: resolveTokenShape
1861
1871
  }, async (args) => {
1862
- const result = await resolveToken(args, cfg, fetch);
1872
+ const result = await resolveToken(args, getConfig(), doFetch);
1863
1873
  return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
1864
1874
  });
1865
1875
  register("candle_execution_status", {
@@ -1867,7 +1877,7 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
1867
1877
  description: "One call before trading: the embedded wallets to spend from, the tier that decides what " + "may be traded, and this key's own spend limits. Reads only; moves nothing. Call it when " + "a run starts, or after an authorization error, rather than inferring readiness from a " + "failed trade. If a read could not be completed the tool says which one and does NOT " + "claim the account is unready: an unreachable endpoint and a missing tier are different " + "problems with different fixes.",
1868
1878
  inputSchema: {}
1869
1879
  }, async () => {
1870
- const result = await executionStatus(cfg, fetch);
1880
+ const result = await executionStatus(getConfig(), doFetch);
1871
1881
  return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
1872
1882
  });
1873
1883
  register("candle_swap", {
@@ -1886,18 +1896,18 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
1886
1896
 
1887
1897
  ` + "This tool spends the embedded wallets. A TEE wallet also bridges, from SOL or USDC to ETH " + "or USDG or back, but only into the same key's TEE wallet on the other chain, with no Candle " + "fee: that runs through `candle swap` with the wallet's bound key, not this tool.",
1888
1898
  inputSchema: swapShape
1889
- }, async (args) => callAndRelay("candle_swap", args, cfg));
1899
+ }, async (args) => callAndRelay("candle_swap", args, getConfig(), doFetch));
1890
1900
  register("candle_transfer", {
1891
1901
  title: "Transfer an asset",
1892
1902
  description: "Move an asset from the account's embedded wallet to one of the account's own wallets, or to an owner-approved withdrawal address. amountRaw 'max' sweeps the spendable balance of that asset.",
1893
1903
  inputSchema: transferShape
1894
- }, async (args) => callAndRelay("candle_transfer", args, cfg));
1904
+ }, async (args) => callAndRelay("candle_transfer", args, getConfig(), doFetch));
1895
1905
  register("candle_sweep", {
1896
1906
  title: "Sweep a wallet",
1897
1907
  description: "Sweep the embedded wallet on one chain to a destination: every base asset (plus any explicitly named mints), one transfer per asset with amountRaw 'max'. Assets with nothing spendable are reported as empty, not errors.",
1898
1908
  inputSchema: sweepShape
1899
1909
  }, async (args) => {
1900
- const result = await executeSweep(args, cfg, fetch);
1910
+ const result = await executeSweep(args, getConfig(), doFetch);
1901
1911
  return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
1902
1912
  });
1903
1913
  register("candle_trade", {
@@ -1920,19 +1930,19 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
1920
1930
  ` + "- If you no longer hold the result, do not re-send to find out what happened. Ask " + "candle_get_operation with the clientTradeId; a 404 there means the trade never reached " + "the rail and nothing moved.",
1921
1931
  inputSchema: tradeShape
1922
1932
  }, async (args) => {
1923
- const result = await executeTrade(args, cfg, fetch);
1933
+ const result = await executeTrade(args, getConfig(), doFetch);
1924
1934
  return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
1925
1935
  });
1926
1936
  register("candle_launch_and_seed", {
1927
1937
  title: "Launch a token and seed it",
1928
- description: "Launch a new token with an optional dev-buy seed bundled into the launch itself, then " + "return the fresh market state and token links in one result. MOVES REAL FUNDS unless " + "dryRun. Seeds above the platform dev-buy ceiling are rejected (DEV_BUY_TOO_HIGH); " + "launch, then top up with candle_trade.",
1938
+ description: "Launch a new token from the account's embedded wallet, with an optional dev-buy seed (in the " + "launch transaction on solana, a best-effort follow-up on hood). Works on every plan, Free included (this is not the atomic launch). Then " + "return the fresh market state and token links in one result. MOVES REAL FUNDS unless " + "dryRun. Seeds above the platform dev-buy ceiling are rejected (DEV_BUY_TOO_HIGH); " + "launch, then top up with candle_trade.",
1929
1939
  inputSchema: launchAndSeedShape
1930
1940
  }, async (args) => {
1931
- const result = await executeLaunchAndSeed(args, cfg, fetch);
1941
+ const result = await executeLaunchAndSeed(args, getConfig(), doFetch);
1932
1942
  return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
1933
1943
  });
1934
1944
  const perpsTool = (tool) => async (args) => {
1935
- const result = await executePerps(tool, args, cfg, env, fetch);
1945
+ const result = await executePerps(tool, args, getConfig(), env, doFetch);
1936
1946
  return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
1937
1947
  };
1938
1948
  const perpsWrite = " MOVES REAL FUNDS on Hyperliquid unless submit is false. Candle builds the action within this key's limits " + "(USD window, maximum leverage, maximum position, slippage and price bands, main-exchange markets only); this " + "server recomputes its hash and checks the action type and Candle's builder before signing, then submits it to " + "Hyperliquid itself. Needs perps:write on the key and CANDLE_KEY_SIGNER_PEM_FILE. A refusal names the limit " + "that refused it (error.limit).";
@@ -1976,7 +1986,7 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
1976
1986
  description: "MOVES REAL FUNDS unless submit is false. Deposit onto the key's Hyperliquid account through Relay: SOL or " + "USDC from its Solana TEE wallet, or ETH or USDG from its Hood TEE wallet, credited as Hyperliquid perps " + "USDC at the key's EVM TEE wallet's own address (Candle resolves it; no recipient is taken). Refused below a " + "floor that covers Hyperliquid's 1 USDC first-deposit charge. No Candle fee. Needs swap:write and a raw cap " + "on the asset, and CANDLE_KEY_SIGNER_PEM_FILE to sign. There is no withdrawal from Hyperliquid yet.",
1977
1987
  inputSchema: perpsDepositShape
1978
1988
  }, async (args) => {
1979
- const result = await executePerpsDeposit(args, cfg, env, fetch);
1989
+ const result = await executePerpsDeposit(args, getConfig(), env, doFetch);
1980
1990
  return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
1981
1991
  });
1982
1992
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@candledottv/mcp",
3
- "version": "0.10.6",
3
+ "version": "0.10.8",
4
4
  "description": "MCP server for the Candle agent rail: launch tokens, read markets, report activity",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -46,7 +46,7 @@
46
46
  "clean": "rm -rf dist"
47
47
  },
48
48
  "dependencies": {
49
- "@modelcontextprotocol/sdk": "1.22.0",
49
+ "@modelcontextprotocol/sdk": "1.26.0",
50
50
  "@noble/hashes": "1.8.0",
51
51
  "zod": "^3.23.0"
52
52
  },