@candledottv/mcp 0.10.14 → 0.10.16
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 +10 -3
- package/dist/index.js +38 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @candledottv/mcp
|
|
2
2
|
|
|
3
3
|
An MCP (Model Context Protocol) server for the Candle agent rail. The current source exposes Candle's REST API as
|
|
4
|
-
forty-
|
|
4
|
+
forty-two tools over stdio, so an MCP-capable agent can launch tokens (optionally seeded with a dev
|
|
5
5
|
buy in the same call), trade, convert between base assets (including across chains), read market
|
|
6
6
|
and feed data, report on-chain activity, and check an agent profile without hand-rolling HTTP
|
|
7
7
|
calls.
|
|
@@ -31,7 +31,7 @@ Environment below -- or skip env editing entirely: install the Candle CLI
|
|
|
31
31
|
environment.
|
|
32
32
|
|
|
33
33
|
`CANDLE_MCP_TOOLS` (optional) is a comma-separated allowlist of tool names; only those register.
|
|
34
|
-
Unset means all forty-
|
|
34
|
+
Unset means all forty-two. An unknown name fails startup with the valid names in the message, rather
|
|
35
35
|
than silently registering the wrong surface. `candle mcp --read-only` / `--tools` set this for
|
|
36
36
|
you.
|
|
37
37
|
|
|
@@ -55,6 +55,12 @@ Both take decimal amounts, never raw base units, and resolve the scale themselve
|
|
|
55
55
|
there the caller genuinely picks the new token's quote pair: `quoteAsset` if given, otherwise
|
|
56
56
|
SOL on Solana and ETH on Hood.
|
|
57
57
|
|
|
58
|
+
Both launch tools also take `mode: "exp-custom"` with `curve: { virtualQuote, threshold }`: a
|
|
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
|
|
62
|
+
[Custom curves](https://docs.candle.tv/developers/headless-launch#custom-curves-choose-v-and-g-switched-off).
|
|
63
|
+
|
|
58
64
|
For a full walkthrough of `candle_launch_and_seed` and `candle_trade`, including the keyless read
|
|
59
65
|
tools, getting a key, funding the embedded wallet, and idempotent retries, see
|
|
60
66
|
[Candle MCP server](https://docs.candle.tv/developers/mcp-server).
|
|
@@ -95,6 +101,7 @@ tools, getting a key, funding the embedded wallet, and idempotent retries, see
|
|
|
95
101
|
| `candle_launch_token` | Launch a token on Candle | `POST /api/v1/launch/headless` (or `/dry-run` when `dryRun: true`); with `imagePath`, `POST /api/v1/uploads/agent-image` first | `CANDLE_AGENT_API_KEY` |
|
|
96
102
|
| `candle_get_market` | Get market state | `GET /api/v1/markets/:chain/:mint` | none |
|
|
97
103
|
| `candle_get_feed` | Get a token feed | `GET /api/v1/markets/feed?bucket=...` | none |
|
|
104
|
+
| `candle_vamp_candidates` | Tickers being copied near graduation: Solana tokens near graduation or graduated in the last 6 hours, most-copied in the last 24 hours first, each with a launch draft (name, ticker, image URL) and `draftNotes` for the fields you must supply. Counts exclude the candidate itself. Answers 404 where the deployment has not switched it on | `GET /api/v1/markets/vamp-candidates?limit=...&minCopies24h=...&stage=...` | `CANDLE_AGENT_API_KEY` (`account:read`) |
|
|
98
105
|
| `candle_token_forensics` | Deployer history, deploy-window buyers, sourced token safety flags | `GET /api/v1/markets/:chain/:mint/forensics` | none |
|
|
99
106
|
| `candle_report_activity` | Report on-chain activity | `POST /api/v1/activity/report` | `CANDLE_AGENT_API_KEY` |
|
|
100
107
|
| `candle_get_agent_profile` | Get an agent profile | `GET /api/v1/users/:idOrWallet/agent` | none |
|
|
@@ -178,7 +185,7 @@ explicitly. Assets with nothing spendable report `empty`; a failed asset never s
|
|
|
178
185
|
## Errors
|
|
179
186
|
|
|
180
187
|
This package never reinterprets an error body, and that body is not one uniform shape across all
|
|
181
|
-
forty-
|
|
188
|
+
forty-two tools:
|
|
182
189
|
|
|
183
190
|
- `candle_launch_token`, `candle_get_market`, and `candle_get_feed` hit endpoints that use the
|
|
184
191
|
structured envelope `{ success: false, error: { code, message, ... } }`. Branch on `error.code`.
|
package/dist/index.js
CHANGED
|
@@ -112,7 +112,7 @@ import { lstat, open, realpath } from "node:fs/promises";
|
|
|
112
112
|
import { dirname, isAbsolute, relative, sep } from "node:path";
|
|
113
113
|
|
|
114
114
|
// src/version.ts
|
|
115
|
-
var SERVER_VERSION = "0.10.
|
|
115
|
+
var SERVER_VERSION = "0.10.16";
|
|
116
116
|
|
|
117
117
|
// src/update-notice.ts
|
|
118
118
|
var PLAIN_VERSION = /^\d+\.\d+\.\d+$/;
|
|
@@ -1652,6 +1652,8 @@ function planTableMarkdown(table) {
|
|
|
1652
1652
|
}
|
|
1653
1653
|
|
|
1654
1654
|
// src/tools.ts
|
|
1655
|
+
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.";
|
|
1656
|
+
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_CURVES and EXP_CUSTOM_CURVES on; " + "elsewhere exp-custom is refused as an unknown mode.";
|
|
1655
1657
|
var localProfileTools = new WeakMap;
|
|
1656
1658
|
var STAKING_DISCLOSURE = "No instant unstake. Read pool.withdrawalTimelockSeconds for the wait. Solana rewards accrue to the holding wallet. Outside the account's main wallet, sCNDL earns no Hood staking rewards; Pro eligibility follows the deployment's tier mode (main-wallet mode excludes it). CNDL in a pending withdrawal counts toward nothing for Pro.";
|
|
1657
1659
|
var stakingWriteShape = {
|
|
@@ -1664,6 +1666,7 @@ var TOOL_NAMES = [
|
|
|
1664
1666
|
"candle_launch_token",
|
|
1665
1667
|
"candle_get_market",
|
|
1666
1668
|
"candle_get_feed",
|
|
1669
|
+
"candle_vamp_candidates",
|
|
1667
1670
|
"candle_token_forensics",
|
|
1668
1671
|
"candle_report_activity",
|
|
1669
1672
|
"candle_get_agent_profile",
|
|
@@ -1792,6 +1795,21 @@ function buildRequest(name, args, cfg) {
|
|
|
1792
1795
|
init: { method: "GET", headers: jsonHeaders() }
|
|
1793
1796
|
};
|
|
1794
1797
|
}
|
|
1798
|
+
case "candle_vamp_candidates": {
|
|
1799
|
+
const apiKey = requireApiKey2(cfg);
|
|
1800
|
+
const { limit, minCopies24h, stage } = args;
|
|
1801
|
+
const params = new URLSearchParams;
|
|
1802
|
+
if (limit !== undefined)
|
|
1803
|
+
params.set("limit", String(limit));
|
|
1804
|
+
if (minCopies24h !== undefined)
|
|
1805
|
+
params.set("minCopies24h", String(minCopies24h));
|
|
1806
|
+
if (stage !== undefined)
|
|
1807
|
+
params.set("stage", stage);
|
|
1808
|
+
return {
|
|
1809
|
+
url: `${base2}/api/v1/markets/vamp-candidates${params.size ? `?${params}` : ""}`,
|
|
1810
|
+
init: { method: "GET", headers: jsonHeaders(apiKey) }
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1795
1813
|
case "candle_report_activity": {
|
|
1796
1814
|
const apiKey = requireApiKey2(cfg);
|
|
1797
1815
|
return {
|
|
@@ -2055,7 +2073,11 @@ var launchTokenShape = {
|
|
|
2055
2073
|
bannerUrl: z2.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. Omit it and the strip falls back to imageUrl"),
|
|
2056
2074
|
chain: z2.string().optional().describe('"solana" or "hood"; defaults to solana'),
|
|
2057
2075
|
quoteAsset: z2.string().optional().describe("Quote asset symbol; defaults per chain"),
|
|
2058
|
-
mode: z2.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"),
|
|
2076
|
+
mode: z2.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"),
|
|
2077
|
+
curve: z2.object({
|
|
2078
|
+
virtualQuote: z2.string().describe('V: the virtual quote the curve starts with, in whole quote units, e.g. "12" SOL'),
|
|
2079
|
+
threshold: z2.string().describe('G: the quote the curve raises before it graduates, in whole quote units, e.g. "40" SOL')
|
|
2080
|
+
}).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"),
|
|
2059
2081
|
stakerAllocationBps: z2.number().optional().describe("Staker allocation in basis points"),
|
|
2060
2082
|
dexVersion: z2.string().nullable().optional().describe('"v3" or "v4"; required for hood launches'),
|
|
2061
2083
|
buyAmount: z2.union([z2.string(), z2.number()]).optional().describe("Initial buy, in quote base units (string or number)"),
|
|
@@ -2122,6 +2144,11 @@ var getOperationShape = {
|
|
|
2122
2144
|
clientId: z2.string().describe("The clientTradeId or clientLaunchId the write used, or that the tool echoed back to you"),
|
|
2123
2145
|
kind: z2.enum(["trade", "launch"]).describe("Which rail the id belongs to. Required: ids are caller-chosen strings with no shape to dispatch on")
|
|
2124
2146
|
};
|
|
2147
|
+
var vampCandidatesShape = {
|
|
2148
|
+
limit: z2.number().optional().describe("Entries to return, 1 to 50 (default 20)"),
|
|
2149
|
+
minCopies24h: z2.number().optional().describe("Only tokens whose ticker was copied at least this many times in the last 24 hours (default 1)"),
|
|
2150
|
+
stage: z2.enum(["final_stretch", "graduated", "all"]).optional().describe("final_stretch: still on the bonding curve; graduated: graduated in the last 6 hours; all (default)")
|
|
2151
|
+
};
|
|
2125
2152
|
var getFeesShape = {
|
|
2126
2153
|
cursor: z2.string().optional().describe("The previous page's continueCursor, verbatim"),
|
|
2127
2154
|
limit: z2.number().optional().describe("Tokens per page, 1 to 50 (default 20). Each one costs chain reads")
|
|
@@ -2221,7 +2248,7 @@ function registerToolSubset(server, options) {
|
|
|
2221
2248
|
const imageDir = configuredImageDir(env);
|
|
2222
2249
|
register("candle_launch_token", {
|
|
2223
2250
|
title: "Launch a token on Candle",
|
|
2224
|
-
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." + (imageDir ? IMAGE_PATH_DISCLOSURE : ""),
|
|
2251
|
+
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. " + CUSTOM_CURVE_RULE + (imageDir ? IMAGE_PATH_DISCLOSURE : ""),
|
|
2225
2252
|
inputSchema: imageDir ? localLaunchTokenShape : launchTokenShape
|
|
2226
2253
|
}, async (args) => {
|
|
2227
2254
|
const image = await resolveLaunchImage(args, imageDir, getConfig(), doFetch);
|
|
@@ -2257,6 +2284,11 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
|
|
|
2257
2284
|
` + "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.",
|
|
2258
2285
|
inputSchema: getFeedShape
|
|
2259
2286
|
}, async (args) => callAndRelay("candle_get_feed", args, getConfig(), doFetch));
|
|
2287
|
+
register("candle_vamp_candidates", {
|
|
2288
|
+
title: "Tickers being copied near graduation",
|
|
2289
|
+
description: VAMP_CANDIDATES_DESCRIPTION,
|
|
2290
|
+
inputSchema: vampCandidatesShape
|
|
2291
|
+
}, async (args) => callAndRelay("candle_vamp_candidates", args, getConfig(), doFetch));
|
|
2260
2292
|
register("candle_report_activity", {
|
|
2261
2293
|
title: "Report on-chain activity",
|
|
2262
2294
|
description: "Report a client-executed transaction (transfer, swap, stake) so Candle records and verifies it.",
|
|
@@ -2467,7 +2499,7 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
|
|
|
2467
2499
|
});
|
|
2468
2500
|
register("candle_launch_and_seed", {
|
|
2469
2501
|
title: "Launch a token and seed it",
|
|
2470
|
-
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." + (imageDir ? IMAGE_PATH_DISCLOSURE : ""),
|
|
2502
|
+
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. " + CUSTOM_CURVE_RULE + (imageDir ? IMAGE_PATH_DISCLOSURE : ""),
|
|
2471
2503
|
inputSchema: imageDir ? localLaunchAndSeedShape : launchAndSeedShape
|
|
2472
2504
|
}, async (args) => {
|
|
2473
2505
|
const result = await executeLaunchAndSeed(args, getConfig(), doFetch, imageDir);
|
|
@@ -2592,6 +2624,8 @@ Never let a MARKET_NOT_FOUND stand in for a clean bill of health. The same rule
|
|
|
2592
2624
|
coverage note on every forensics measurement: "unavailable" is NOT "clean" — say so rather than
|
|
2593
2625
|
reporting a token as safe.
|
|
2594
2626
|
|
|
2627
|
+
LAUNCHING A COPY: candle_vamp_candidates (a key with account:read) lists Solana tickers being copied near graduation, each with a launch draft for candle_launch_token or candle_launch_and_seed. Supply any draft field that is null (draftNotes says why), and launch with dryRun: true first. Where the deployment has not switched it on it answers 404.
|
|
2628
|
+
|
|
2595
2629
|
WRITING (trade, launch, transfer, sweep, swap) needs a key. If a call returns an auth error, the fix is on the human's side: they run \`candle auth login\`, which authorizes a device in the browser and stores the credentials. Do not ask them to paste a key into a config file, and do not retry the call until they confirm.
|
|
2596
2630
|
|
|
2597
2631
|
Two chains: solana and hood. Most tools take an explicit chain — resolve it with candle_resolve_token rather than guessing.
|