@candledottv/mcp 0.10.9 → 0.10.11
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 +7 -5
- package/dist/index.js +51 -6
- 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. It exposes Candle's REST API as
|
|
4
|
-
|
|
4
|
+
thirty-one 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
|
|
34
|
+
Unset means all thirty-one. 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
|
|
|
@@ -98,8 +98,8 @@ tools, getting a key, funding the embedded wallet, and idempotent retries, see
|
|
|
98
98
|
| `candle_trade` | Buy or sell a token | Reads the market for its decimals (or wallet balance, for a percent sell) then `POST /api/v1/trade/agent/build` | `CANDLE_AGENT_API_KEY` (`swap:write`) |
|
|
99
99
|
| `candle_launch_and_seed` | Launch a token and seed it | `POST /api/v1/launch/headless` (or `/dry-run`), then a follow-up `GET /api/v1/markets/:chain/:mint` | `CANDLE_AGENT_API_KEY` |
|
|
100
100
|
| `candle_swap` | Swap between base assets | `POST /api/v1/agent/swap` | `CANDLE_AGENT_API_KEY` (`swap:write`) |
|
|
101
|
-
| `candle_transfer` | Transfer an asset | `POST /api/v1/agent/transfer` | `CANDLE_AGENT_API_KEY
|
|
102
|
-
| `candle_sweep` | Sweep a wallet to one destination | One `POST /api/v1/agent/transfer` per asset, `amountRaw: "max"` | `CANDLE_AGENT_API_KEY
|
|
101
|
+
| `candle_transfer` | Transfer an asset | `POST /api/v1/agent/transfer` | `CANDLE_AGENT_API_KEY`, Withdraw (`transfer:write`) |
|
|
102
|
+
| `candle_sweep` | Sweep a wallet to one destination | One `POST /api/v1/agent/transfer` per asset, `amountRaw: "max"` | `CANDLE_AGENT_API_KEY`, Withdraw (`transfer:write`) |
|
|
103
103
|
| `candle_resolve_token` | Turn a bare mint or contract address into Candle's market for it | `GET /api/v1/markets/:chain/:mint` | none |
|
|
104
104
|
| `candle_get_wallets` | The account's embedded wallets, one per chain, with delegation state | `GET /api/v1/agent/wallets/embedded` | `CANDLE_AGENT_API_KEY` |
|
|
105
105
|
| `candle_get_profile_wallets` | Which wallets an agent profile may spend from, and whether it is scoped | `GET /api/v1/agent/keys/:prefix/wallets` | `CANDLE_AGENT_API_KEY` |
|
|
@@ -109,6 +109,8 @@ tools, getting a key, funding the embedded wallet, and idempotent retries, see
|
|
|
109
109
|
| `candle_get_portfolio` | What the account's embedded and TEE wallets hold on Solana and Hood (Hood in `hood`), with prices; unpriced holdings say why, never zero | `GET /api/v1/agent/portfolio` | `CANDLE_AGENT_API_KEY` (`account:read`) |
|
|
110
110
|
| `candle_execution_status` | One call before trading: wallets to spend from, tier, and whether trading is possible | Composes the wallet and tier reads | `CANDLE_AGENT_API_KEY` |
|
|
111
111
|
| `candle_get_operation` | Look up a trade or launch by the id its write used, and whether it landed | `GET /api/v1/trade/agent/jobs/:clientId` or `/api/v1/launch/headless/jobs/:clientId` | `CANDLE_AGENT_API_KEY` |
|
|
112
|
+
| `candle_get_fees` | The creator fees the account's launched tokens can pay: raw amounts from chain reads, and the reason a token cannot be claimed | `GET /api/v1/agent/fees` | `CANDLE_AGENT_API_KEY` (`account:read`) |
|
|
113
|
+
| `candle_claim_fees` | Collect one token's creator fees to the wallet fixed at launch (no destination argument); `dryRun` sends nothing. Answers `LAUNCH_DISABLED` until the server enables agent claims | `POST /api/v1/agent/fees/:mint/claim` | `CANDLE_AGENT_API_KEY` (`launch:write`) |
|
|
112
114
|
| `candle_perps_setup` | Approve Candle's Hyperliquid builder fee once; report the account's mode and balance | `POST /api/v1/agent/perps/setup`, then relay sign and Hyperliquid submit | `CANDLE_AGENT_API_KEY` (`perps:write`), `CANDLE_KEY_SIGNER_PEM_FILE` |
|
|
113
115
|
| `candle_perps_open` | Open or add to a perps position, with optional take-profit and stop-loss | `POST /api/v1/agent/perps/open`, checked here, relay-signed, submitted to Hyperliquid | `CANDLE_AGENT_API_KEY` (`perps:write`), `CANDLE_KEY_SIGNER_PEM_FILE` |
|
|
114
116
|
| `candle_perps_close` | Close all or part of a perps position, reduce-only | `POST /api/v1/agent/perps/close`, the same | `CANDLE_AGENT_API_KEY` (`perps:write`), `CANDLE_KEY_SIGNER_PEM_FILE` |
|
|
@@ -133,7 +135,7 @@ explicitly. Assets with nothing spendable report `empty`; a failed asset never s
|
|
|
133
135
|
## Errors
|
|
134
136
|
|
|
135
137
|
This package never reinterprets an error body, and that body is not one uniform shape across all
|
|
136
|
-
|
|
138
|
+
thirty-one tools:
|
|
137
139
|
|
|
138
140
|
- `candle_launch_token`, `candle_get_market`, and `candle_get_feed` hit endpoints that use the
|
|
139
141
|
structured envelope `{ success: false, error: { code, message, ... } }`. Branch on `error.code`.
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
6
|
|
|
7
|
+
// src/market-disclosure.ts
|
|
8
|
+
var EXTERNAL_MARKET_DISCLOSURE = "For external tokens (external: true), buysOpen, sellsOpen, quoteMint, feeBps, venue, " + "graduationVenue and poolAddress are compatibility placeholders, not measured facts about " + "trading availability, pool pairing, fees or pool venue; trade.routable is stored eligibility " + "and does not verify those fields.";
|
|
9
|
+
|
|
7
10
|
// src/tools.ts
|
|
8
11
|
import { z as z2 } from "zod";
|
|
9
12
|
|
|
@@ -106,7 +109,7 @@ function percentOfBalance(balanceRaw, percent) {
|
|
|
106
109
|
import { randomUUID } from "node:crypto";
|
|
107
110
|
|
|
108
111
|
// src/version.ts
|
|
109
|
-
var SERVER_VERSION = "0.10.
|
|
112
|
+
var SERVER_VERSION = "0.10.11";
|
|
110
113
|
|
|
111
114
|
// src/update-notice.ts
|
|
112
115
|
var PLAIN_VERSION = /^\d+\.\d+\.\d+$/;
|
|
@@ -1447,6 +1450,8 @@ var TOOL_NAMES = [
|
|
|
1447
1450
|
"candle_resolve_token",
|
|
1448
1451
|
"candle_execution_status",
|
|
1449
1452
|
"candle_get_operation",
|
|
1453
|
+
"candle_get_fees",
|
|
1454
|
+
"candle_claim_fees",
|
|
1450
1455
|
"candle_perps_setup",
|
|
1451
1456
|
"candle_perps_open",
|
|
1452
1457
|
"candle_perps_close",
|
|
@@ -1569,6 +1574,27 @@ function buildRequest(name, args, cfg) {
|
|
|
1569
1574
|
const path = kind === "launch" ? `/api/v1/launch/headless/jobs/${encodeURIComponent(clientId)}` : `/api/v1/trade/agent/jobs/${encodeURIComponent(clientId)}`;
|
|
1570
1575
|
return { url: `${base2}${path}`, init: { method: "GET", headers: jsonHeaders(apiKey) } };
|
|
1571
1576
|
}
|
|
1577
|
+
case "candle_get_fees": {
|
|
1578
|
+
const apiKey = requireApiKey2(cfg);
|
|
1579
|
+
const { cursor, limit } = args;
|
|
1580
|
+
const params = new URLSearchParams;
|
|
1581
|
+
if (cursor !== undefined)
|
|
1582
|
+
params.set("cursor", cursor);
|
|
1583
|
+
if (limit !== undefined)
|
|
1584
|
+
params.set("limit", String(limit));
|
|
1585
|
+
return {
|
|
1586
|
+
url: `${base2}/api/v1/agent/fees${params.size ? `?${params}` : ""}`,
|
|
1587
|
+
init: { method: "GET", headers: jsonHeaders(apiKey) }
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
case "candle_claim_fees": {
|
|
1591
|
+
const apiKey = requireApiKey2(cfg);
|
|
1592
|
+
const { mint, dryRun } = args;
|
|
1593
|
+
return {
|
|
1594
|
+
url: `${base2}/api/v1/agent/fees/${encodeURIComponent(mint)}/claim`,
|
|
1595
|
+
init: { method: "POST", headers: jsonHeaders(apiKey), body: JSON.stringify({ dryRun: dryRun === true }) }
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1572
1598
|
case "candle_get_wallets": {
|
|
1573
1599
|
const apiKey = requireApiKey2(cfg);
|
|
1574
1600
|
return {
|
|
@@ -1686,7 +1712,7 @@ var feedLimit = z2.preprocess((value) => {
|
|
|
1686
1712
|
return /^[0-9]+$/.test(trimmed) ? Number(trimmed) : value;
|
|
1687
1713
|
}, z2.number({ invalid_type_error: FEED_LIMIT_MESSAGE }).int(FEED_LIMIT_MESSAGE).min(1, FEED_LIMIT_MESSAGE).max(200, FEED_LIMIT_MESSAGE).optional().describe("Max rows to return, an integer from 1 to 200."));
|
|
1688
1714
|
var getFeedShape = {
|
|
1689
|
-
bucket: z2.enum(["new", "graduated", "onfire", "bluechip"]),
|
|
1715
|
+
bucket: z2.enum(["new", "graduated", "onfire", "bluechip", "finalstretch"]).describe("finalstretch: pump.fun tokens at 50% or more of their bonding curve, most complete first; no delta, read the whole list. Where it is not switched on it is refused as an unknown bucket: use another bucket. bluechip is deprecated and will be removed."),
|
|
1690
1716
|
chain: z2.string().optional().describe("Optional chain filter"),
|
|
1691
1717
|
where: z2.string().optional().describe('JSON filter, e.g. {"marketCap":{"lt":150000},"liquidityUsd":{"gte":25000},"mintAuthorityDisabled":{"eq":true}}. ' + "Comparators: eq, ne, lt, lte, gt, gte, present. An ABSENT field satisfies none of them except " + "present:false, so a filter for mintAuthorityDisabled eq true returns only tokens that actually say " + "true, never ones where the flag is simply missing. Use present:false to find the tokens with no data."),
|
|
1692
1718
|
sort: z2.string().optional().describe('Sort as "field" or "field:asc" / "field:desc". A bare field means desc.'),
|
|
@@ -1704,6 +1730,14 @@ var getOperationShape = {
|
|
|
1704
1730
|
clientId: z2.string().describe("The clientTradeId or clientLaunchId the write used, or that the tool echoed back to you"),
|
|
1705
1731
|
kind: z2.enum(["trade", "launch"]).describe("Which rail the id belongs to. Required: ids are caller-chosen strings with no shape to dispatch on")
|
|
1706
1732
|
};
|
|
1733
|
+
var getFeesShape = {
|
|
1734
|
+
cursor: z2.string().optional().describe("The previous page's continueCursor, verbatim"),
|
|
1735
|
+
limit: z2.number().optional().describe("Tokens per page, 1 to 50 (default 20). Each one costs chain reads")
|
|
1736
|
+
};
|
|
1737
|
+
var claimFeesShape = {
|
|
1738
|
+
mint: z2.string().describe("The launched token: a Solana mint or a Hood token address, as candle_get_fees lists it"),
|
|
1739
|
+
dryRun: z2.boolean().optional().describe("Check and read only. Sends nothing and writes nothing. Use it first to see what would be claimed")
|
|
1740
|
+
};
|
|
1707
1741
|
var resolveTokenShape = {
|
|
1708
1742
|
mint: z2.string().describe("Token mint (Solana, base58) or contract address (Hood, 0x-prefixed)")
|
|
1709
1743
|
};
|
|
@@ -1783,7 +1817,7 @@ function registerToolSubset(server, options) {
|
|
|
1783
1817
|
}, async (args) => callAndRelay("candle_launch_token", args, getConfig(), doFetch));
|
|
1784
1818
|
register("candle_get_market", {
|
|
1785
1819
|
title: "Get market state",
|
|
1786
|
-
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.",
|
|
1820
|
+
description: "Read Candle and indexed external markets, including indexed-but-not-routable tokens. No key needed. " + EXTERNAL_MARKET_DISCLOSURE + " " + "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.",
|
|
1787
1821
|
inputSchema: getMarketShape
|
|
1788
1822
|
}, async (args) => callAndRelay("candle_get_market", args, getConfig(), doFetch));
|
|
1789
1823
|
register("candle_token_forensics", {
|
|
@@ -1795,7 +1829,7 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
|
|
|
1795
1829
|
}, async (args) => callAndRelay("candle_token_forensics", args, getConfig(), doFetch));
|
|
1796
1830
|
register("candle_get_feed", {
|
|
1797
1831
|
title: "Get a token feed",
|
|
1798
|
-
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.
|
|
1832
|
+
description: "Read one of the trade page's public feeds: new, graduated, onfire, or finalstretch " + "(bluechip still answers, is deprecated, and will be removed; where finalstretch is not " + "switched on it is refused as an unknown bucket, so use another bucket). Reads " + `only; moves nothing. No key needed. Start here when nobody has named a token.
|
|
1799
1833
|
|
|
1800
1834
|
` + "This indexes the WIDER market, not just Candle's own launches. Rows carry launchpad and " + "discovery flags (jupiterOk, externalTradeable, paperDiscoveryOk, organic0LiveOk). " + "candle_get_market resolves Jupiter-indexed rows; do not hard-skip organicScore=0 on live " + "when organic0LiveOk is true. liquidityDrawdownBps is a signal only — nothing here blocks " + `resolve or trade on drain; filter with where if you want to avoid draining pools.
|
|
1801
1835
|
|
|
@@ -1843,6 +1877,16 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
|
|
|
1843
1877
|
` + "Amounts come back RAW, deliberately: the answer you need after a timeout is the outcome, " + "and you already know what you asked for.",
|
|
1844
1878
|
inputSchema: getOperationShape
|
|
1845
1879
|
}, async (args) => callAndRelay("candle_get_operation", args, getConfig(), doFetch));
|
|
1880
|
+
register("candle_get_fees", {
|
|
1881
|
+
title: "Read the creator fees this account's tokens can pay",
|
|
1882
|
+
description: "The tokens launched by the account's own wallets, with what each could pay now in creator fees " + "(raw units from direct chain reads) and `claimable: true` or the `reason` it is not. Reads only; " + "moves nothing. It returns no earned or paid-out totals. An unreadable token is marked, never " + "shown as zero. Paged: pass `continueCursor` back as `cursor`.",
|
|
1883
|
+
inputSchema: getFeesShape
|
|
1884
|
+
}, async (args) => callAndRelay("candle_get_fees", args, getConfig(), doFetch));
|
|
1885
|
+
register("candle_claim_fees", {
|
|
1886
|
+
title: "Claim a token's creator fees",
|
|
1887
|
+
description: "Collect one token's creator fees and pay them to the wallet fixed at launch. There is no destination " + "argument and the tool cannot send funds anywhere else. Candle signs with the embedded wallet, which " + "pays the network fee. Needs launch:write, and the server keeps it off until it is enabled (LAUNCH_DISABLED). " + "Call it with dryRun: true first; a real claim moves real funds into the recipient wallet, which may be " + "the wallet this agent trades from. Not idempotent by id: if the result is lost, read candle_get_fees " + "before claiming again.",
|
|
1888
|
+
inputSchema: claimFeesShape
|
|
1889
|
+
}, async (args) => callAndRelay("candle_claim_fees", args, getConfig(), doFetch));
|
|
1846
1890
|
register("candle_get_wallets", {
|
|
1847
1891
|
title: "List the wallets Candle executes with",
|
|
1848
1892
|
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.",
|
|
@@ -1875,7 +1919,7 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
|
|
|
1875
1919
|
}, async () => callAndRelay("candle_get_portfolio", {}, getConfig(), doFetch));
|
|
1876
1920
|
register("candle_resolve_token", {
|
|
1877
1921
|
title: "Resolve a contract address to a token",
|
|
1878
|
-
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.",
|
|
1922
|
+
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. " + EXTERNAL_MARKET_DISCLOSURE,
|
|
1879
1923
|
inputSchema: resolveTokenShape
|
|
1880
1924
|
}, async (args) => {
|
|
1881
1925
|
const result = await resolveToken(args, getConfig(), doFetch);
|
|
@@ -2005,7 +2049,7 @@ var INSTRUCTIONS = `Candle is a trading and token-launch rail for agents. You ho
|
|
|
2005
2049
|
|
|
2006
2050
|
START HERE — six tools need NO credential. Call these first to confirm the server is wired before asking anyone for anything:
|
|
2007
2051
|
candle_get_market price, market cap, volume, curve state for one token
|
|
2008
|
-
candle_get_feed the roster: hot streak, new pairs, graduated, blue chip
|
|
2052
|
+
candle_get_feed the roster: hot streak, new pairs, graduated, final stretch, blue chip (final stretch is refused where it is not switched on; use another bucket)
|
|
2009
2053
|
candle_resolve_token a ticker or partial name -> mint address + chain
|
|
2010
2054
|
candle_token_forensics call this before quoting or buying. Returns the on-chain developer (never a launchpad shared authority; deployer.attribution names the launchpad or issuer when no developer is on chain), their went-to-zero rate and last coins, who bought in the deploy window (strangers in the same slot are the bundle signal), same-funder insider share, same-funder cluster, and safety.summary with six sourced flags. Refuse an unprompted buy when flagged; incomplete or unknown is not clearance. launch.deployerLaunches is an inclusive informational count, never a warning
|
|
2011
2055
|
candle_get_agent_profile your own tier, caps and verified activity
|
|
@@ -2020,6 +2064,7 @@ only when organic0LiveOk is false.
|
|
|
2020
2064
|
candle_get_market and candle_resolve_token answer for same-chain indexed external mints even
|
|
2021
2065
|
when trade.routable is false. Read candleLaunched, launchpad, venue and trade; jupiterOk and
|
|
2022
2066
|
paperDiscoveryOk are separate signals. Identity-only Solana reads have route_unverified.
|
|
2067
|
+
${EXTERNAL_MARKET_DISCLOSURE}
|
|
2023
2068
|
General quotes use POST /api/v1/trade/agent/quote; curve quotes/lifecycle describe Candle launches.
|
|
2024
2069
|
Stored eligibility is not a successful quote or permission. MARKET_NOT_FOUND remains a legacy
|
|
2025
2070
|
code: read error.routing.reason, error.discovery and sibling error.retryable. A curve-only
|