@candledottv/mcp 0.10.29 → 0.10.30
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 +73 -19
- package/dist/index.js +300 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
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
|
-
eighty-
|
|
4
|
+
eighty-seven tools over stdio, plus four Hyperliquid market reads (eighty-eight in all), 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, read one token's signals and tracked-wallet trades and its card data, report on-chain activity,
|
|
7
7
|
check an agent profile, and backtest a strategy in hacc's strategy lab without hand-rolling HTTP calls. The five
|
|
8
8
|
strategy lab tools are unreleased source.
|
|
9
9
|
|
|
10
|
+
Not sure this is the right tool? [How to choose: SDK, MCP or CLI](https://docs.candle.tv/developers/choose-a-surface).
|
|
11
|
+
|
|
10
12
|
## Try it without a key
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
production): `candle_get_market`, `candle_get_feed`, `candle_token_events`, `candle_token_card`, `candle_token_history`,
|
|
14
|
+
Nineteen tools are read-only and need nothing but `CANDLE_API_URL` (which already defaults to
|
|
15
|
+
production): `candle_get_market`, `candle_get_price_history`, `candle_get_feed`, `candle_token_events`, `candle_token_card`, `candle_token_history`,
|
|
14
16
|
`candle_token_page_about`, `candle_token_page_burns`, `candle_token_page_history`, `candle_token_page_updates`, `candle_token_page_comments`, `candle_token_forensics`, `candle_token_clusters`,
|
|
15
17
|
`candle_get_agent_profile`, `candle_list_agents`, `candle_list_agent_posts`, `candle_get_agent_positions`, `candle_resolve_token` and `candle_get_plans`.
|
|
16
|
-
|
|
17
|
-
With
|
|
18
|
+
All of them but `candle_get_price_history` (new in source, not yet published) are in the published 0.10.29; `candle_get_agent_positions` is new since 0.10.27.
|
|
19
|
+
With token-page reads enabled on the deployment, those nineteen work immediately without a key.
|
|
18
20
|
Install the released server with no `env` block at all and its ten public reads work immediately:
|
|
19
21
|
|
|
20
22
|
```json
|
|
@@ -34,8 +36,10 @@ Environment below -- or skip env editing entirely: install the Candle CLI
|
|
|
34
36
|
`candle auth login`, and `candle mcp` launches this server with the stored key and API URL in its
|
|
35
37
|
environment.
|
|
36
38
|
|
|
39
|
+
To mint a key for another agent without keeping it as the CLI profile's working key, run `candle keys create --access write --label agent-one --no-store` after device login. The key is printed once; put it in the intended agent's secret store. `--json` reports `stored: false` and exits 0. The profile's existing working key, prefix and scopes stay unchanged, even when it has no working key. Add `--embedded-wallet allow` at a terminal if this key needs the account's embedded wallet. This is a CLI storage option; it adds no MCP tool and does not change tool counts.
|
|
40
|
+
|
|
37
41
|
`CANDLE_MCP_TOOLS` (optional) is a comma-separated allowlist of tool names; only those register.
|
|
38
|
-
Unset means all eighty-
|
|
42
|
+
Unset means all eighty-seven. An unknown name fails startup with the valid names in the message, rather
|
|
39
43
|
than silently registering the wrong surface. `candle mcp --read-only` / `--tools` set this for
|
|
40
44
|
you.
|
|
41
45
|
|
|
@@ -52,8 +56,8 @@ Both take decimal amounts, never raw base units, and resolve the scale themselve
|
|
|
52
56
|
- A `candle_trade` **buy** is denominated in the token's OWN quote asset, whatever it was launched
|
|
53
57
|
against (SOL for a SOL-launched token, USDC or CNDL for those quote pairs). The tool reads the
|
|
54
58
|
market first and converts against its `quoteDecimals`; the `quoteAsset` field applies only to a
|
|
55
|
-
mint Candle never launched, the path traded through Jupiter (buying
|
|
56
|
-
|
|
59
|
+
mint Candle never launched, the path traded through Jupiter (buying and selling work on any
|
|
60
|
+
plan). A
|
|
57
61
|
**sell** is denominated in tokens and converts against the market's own `decimals`.
|
|
58
62
|
- A `candle_launch_and_seed` `devBuy` is denominated in the quote asset that launch selects, since
|
|
59
63
|
there the caller genuinely picks the new token's quote pair: `quoteAsset` if given, otherwise
|
|
@@ -71,6 +75,13 @@ rest, and omitted uses `curve_price` when allocations fit, otherwise `legacy`. `
|
|
|
71
75
|
`discord`. See
|
|
72
76
|
[Custom curves](https://docs.candle.tv/developers/headless-launch#custom-curves-choose-v-and-g-switched-off).
|
|
73
77
|
|
|
78
|
+
On Solana, where the API has `CURVE_LAUNCH_RULES_ENABLED` on, both launch tools take
|
|
79
|
+
`launchRules`, the request's nested object in seconds and raw units like their other amounts:
|
|
80
|
+
`openDelaySecs`, `maxBuyPerWalletRaw` with `capWindowSecs`, `allowlist` with
|
|
81
|
+
`allowlistWindowSecs`, and `devBuyMaxRaw`. The API checks the bounds and answers
|
|
82
|
+
`LAUNCH_RULES_INVALID` with the field and the reason. See
|
|
83
|
+
[Launch rules](https://docs.candle.tv/developers/headless-launch#launch-rules-solana).
|
|
84
|
+
|
|
74
85
|
For a full walkthrough of `candle_launch_and_seed` and `candle_trade`, including the keyless read
|
|
75
86
|
tools, getting a key, funding the embedded wallet, and idempotent retries, see
|
|
76
87
|
[Candle MCP server](https://docs.candle.tv/developers/mcp-server).
|
|
@@ -85,7 +96,7 @@ tools, getting a key, funding the embedded wallet, and idempotent retries, see
|
|
|
85
96
|
non-loopback host. For a trusted local endpoint that is not loopback, such as a devcontainer
|
|
86
97
|
reaching its host; not for anything that leaves the machine.
|
|
87
98
|
- `CANDLE_AGENT_API_KEY` -- an agent API key (`cndl_live_...` / `cndl_test_...`), issued from a
|
|
88
|
-
Candle account's agent settings page. Required by every tool except the
|
|
99
|
+
Candle account's agent settings page. Required by every tool except the nineteen read-only ones
|
|
89
100
|
listed above, which includes the account-scoped reads (`candle_get_wallets`,
|
|
90
101
|
`candle_execution_status`, `candle_get_operation`) as well as the writes; those eighteen
|
|
91
102
|
work without it, so the server is useful the moment it is installed and only asks for a key when
|
|
@@ -114,6 +125,7 @@ tools, getting a key, funding the embedded wallet, and idempotent retries, see
|
|
|
114
125
|
| --- | --- | --- | --- |
|
|
115
126
|
| `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` |
|
|
116
127
|
| `candle_get_market` | Get market state | `GET /api/v1/markets/:chain/:mint` | none |
|
|
128
|
+
| `candle_get_price_history` | One Solana token's OHLC bars in USD per token, oldest first (interval 1m-1d, limit 1-500, `to` unix seconds); `source` and `volumeUnit` say whose bars. Hood is refused with its REST route | `GET /api/v1/markets/solana/:mint/candles` | none |
|
|
117
129
|
| `candle_get_feed` | Get a token feed | `GET /api/v1/markets/feed?bucket=...` | none |
|
|
118
130
|
| `candle_token_edit` | As the token's creator, set Candle's copy of its description and links; an unknown `socials` key is refused locally. Needs a `launch:write` key from the launching account; reuse `idempotencyKey` to retry. Off until the deployment switches it on (503 `METADATA_EDIT_DISABLED`) | `PATCH /api/v1/tokens/:chain/:mint/metadata` | `x-api-key` |
|
|
119
131
|
| `candle_token_history` | A token's metadata edits, newest first, with the previous values | `GET /api/v1/tokens/:chain/:mint/metadata/history?limit=...&cursor=...` | none |
|
|
@@ -157,11 +169,14 @@ tools, getting a key, funding the embedded wallet, and idempotent retries, see
|
|
|
157
169
|
| `candle_get_wallets` | The account's embedded wallets, one per chain, with delegation state | `GET /api/v1/agent/wallets/embedded` | `CANDLE_AGENT_API_KEY` |
|
|
158
170
|
| `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` |
|
|
159
171
|
| `candle_set_profile_wallets` | Replace the wallets an agent profile may spend from | `PUT /api/v1/agent/keys/:prefix/wallets` | `CANDLE_AGENT_API_KEY` |
|
|
160
|
-
| `candle_get_profile_pnl` | An agent profile's share of the account's P&L, the console's figures to the cent: realized on its sales, fees, and the open positions that belong to it at cost basis, marked at current prices (unpriced positions counted, not zeroed), with `totalUsd`, the closed positions as made, lost and partial sells (`closed`), dust flagged and counted apart (`openPositionsExDust`), split by wallet (`pnl.byWallet`, summing to the total) and by chain (`pnl.byChain`), with `chain` and `wallet` on every position | `GET /api/v1/agent/keys/:prefix/pnl` | `CANDLE_AGENT_API_KEY` |
|
|
161
|
-
| `candle_get_profile_trades` | An agent profile's orders, fills, fees and transaction hashes | `GET /api/v1/agent/keys/:prefix/trades` | `CANDLE_AGENT_API_KEY` |
|
|
172
|
+
| `candle_get_profile_pnl` | An agent profile's share of the account's P&L, the console's figures to the cent: realized on its sales, fees, and the open positions that belong to it at cost basis, marked at current prices (unpriced positions counted, not zeroed), with `totalUsd`, the closed positions as made, lost and partial sells (`closed`), dust flagged and counted apart (`openPositionsExDust`), split by wallet (`pnl.byWallet`, summing to the total) and by chain (`pnl.byChain`), with `chain` and `wallet` on every position; `sinceMs` adds a `window` of realized and open mark changes, including older holdings (see "P&L since a time") | `GET /api/v1/agent/keys/:prefix/pnl` | `CANDLE_AGENT_API_KEY` |
|
|
173
|
+
| `candle_get_profile_trades` | An agent profile's orders, fills, fees and transaction hashes; page by time with `since`, `until` and `cursor` | `GET /api/v1/agent/keys/:prefix/trades` | `CANDLE_AGENT_API_KEY` |
|
|
174
|
+
| `candle_get_profile_paper` | An agent profile's PAPER P&L and 50 latest paper trades, from quotes (`basis: "quote"`), never live figures | `GET /api/v1/agent/keys/:prefix/paper` | `CANDLE_AGENT_API_KEY` |
|
|
175
|
+
| `candle_get_paper_inventory` | This key's paper positions in raw units (needs `swap:write`) | `GET /api/v1/trade/agent/paper/inventory` | `CANDLE_AGENT_API_KEY` |
|
|
176
|
+
| `candle_get_account_pnl` | P&L for every agent profile on the account in one read (`candle pnl` without `--profile`) | `GET /api/v1/agent/keys/pnl` | `CANDLE_AGENT_API_KEY` |
|
|
162
177
|
| `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`) |
|
|
163
|
-
| `candle_execution_status` | One call before trading: wallets to spend from, tier, and
|
|
164
|
-
| `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` |
|
|
178
|
+
| `candle_execution_status` | One call before trading: wallets to spend from, tier, this key's limits (`limits.txLimitUse` has its USD limit use and when it resets), and its loss limits and stop state (a stopped key is `ready: false` with a top-level `stopped`) | Composes the wallet, tier, `GET /api/v1/agent/keys/self/limits` and `GET /api/v1/agent/keys/self/risk` reads | `CANDLE_AGENT_API_KEY` |
|
|
179
|
+
| `candle_get_operation` | Look up a trade, swap or launch by the id its write used, and whether it landed. `kind: "swap"` reads a `candle_swap` by its `clientSwapId` and answers `job.outcome` (`in_flight`, `confirmed`, `failed`, `partial` or `indeterminate`) | `GET /api/v1/trade/agent/jobs/:clientId`, `/api/v1/agent/swap/by-client-id/:clientId` or `/api/v1/launch/headless/jobs/:clientId` | `CANDLE_AGENT_API_KEY` |
|
|
165
180
|
| `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`) |
|
|
166
181
|
| `candle_atomic_launch_dry_run` | Check a Solana launch with first buys and price each buy on the curve it would deploy; builds, reserves and writes nothing | `POST /api/v1/launch/atomic/dry-run` | `CANDLE_AGENT_API_KEY` (`launch:write`, `swap:write`, Pro or Max) |
|
|
167
182
|
| `candle_claim_fees` | Collect one token's creator fees to the wallet fixed at launch (no destination argument); `dryRun` sends nothing | `POST /api/v1/agent/fees/:mint/claim` | `CANDLE_AGENT_API_KEY` (`launch:write`) |
|
|
@@ -240,7 +255,7 @@ explicitly. Assets with nothing spendable report `empty`; a failed asset never s
|
|
|
240
255
|
## Errors
|
|
241
256
|
|
|
242
257
|
This package never reinterprets an error body, and that body is not one uniform shape across all
|
|
243
|
-
eighty-
|
|
258
|
+
eighty-seven tools:
|
|
244
259
|
|
|
245
260
|
- `candle_token_events`, called with `kind`, is the one narrowing of a SUCCESS body, never an
|
|
246
261
|
error one: the relayed `events` are filtered to that kind and the body gains `filteredByKind`.
|
|
@@ -410,10 +425,9 @@ Every JSON result carries `network`, epoch-ms `readAt`, `gate: { minOpenInterest
|
|
|
410
425
|
|
|
411
426
|
SDK and local MCP cache market reads for five seconds; book and candle snapshots are never cached. CLI/MCP select testnet with `CANDLE_HYPERLIQUID_NETWORK=testnet`; SDK uses `hyperliquidNetwork: "testnet"`. These four tools are local-only and are absent from hosted MCP tool sets. MCP uses the existing `CANDLE_API_KEY` or `CANDLE_AGENT_API_KEY` configuration, with no signer PEM required. No signing, relay or `/exchange` request occurs.
|
|
412
427
|
|
|
413
|
-
## Token page tools (
|
|
428
|
+
## Token page tools (local clients)
|
|
414
429
|
|
|
415
|
-
The sixteen `candle_token_page_*` tools below are
|
|
416
|
-
`candle mcp` source. They require a human-owned package release before npm installs receive them.
|
|
430
|
+
The sixteen `candle_token_page_*` tools below are in the published 0.10.29 and the bundled `candle mcp`.
|
|
417
431
|
They are not offered by the hosted OAuth connector. Existing OAuth consent and key grants are
|
|
418
432
|
unchanged; local keys need explicit `token-pages:read` or `token-pages:write` scopes. All token-page
|
|
419
433
|
switches default off: `404`/`feature_off` is a refusal, not an empty page or permission grant.
|
|
@@ -448,7 +462,7 @@ removed or unsafe versions cannot be restored. The four reads have read-only MCP
|
|
|
448
462
|
and content mutations have mutation hints and approval classifications; content approvals retain
|
|
449
463
|
`idempotencyKey` as their operation ID. The server never enables a deployment switch.
|
|
450
464
|
|
|
451
|
-
Token update tools
|
|
465
|
+
Token update tools:
|
|
452
466
|
|
|
453
467
|
| Tool | Behavior |
|
|
454
468
|
| --- | --- |
|
|
@@ -462,7 +476,7 @@ All three mutations require `token-pages:write` and a durable `idempotencyKey`.
|
|
|
462
476
|
Update bodies cannot be edited. Reporting and moderation apply to their exact post IDs.
|
|
463
477
|
These tools grant no notification delivery or activation permission.
|
|
464
478
|
|
|
465
|
-
Token comment tools
|
|
479
|
+
Token comment tools:
|
|
466
480
|
|
|
467
481
|
| Tool | Behavior |
|
|
468
482
|
| --- | --- |
|
|
@@ -472,3 +486,43 @@ Token comment tools in unreleased source:
|
|
|
472
486
|
A comment uses the same eligibility, restriction and idempotency rules as a section edit. It is
|
|
473
487
|
never pushed to holders and cannot be edited. `candle_token_page_post_delete` removes the key
|
|
474
488
|
account's own comment, including after selling; no other account can. Flags apply to its exact ID.
|
|
489
|
+
|
|
490
|
+
## Rate-limit cost per tool
|
|
491
|
+
|
|
492
|
+
Every tool spends from the key's per-minute limit (`tierCaps.rateLimitPerMin` on `GET /api/v1/agent/tier`; Free 30, Pro 300, Max 600). Over `candle mcp` (stdio) a tool costs one per REST request it makes: `candle_execution_status` makes 4, and `candle_get_portfolio`, `candle_get_profile_pnl` and `candle_get_wallets` make 1 each. The hosted connector charges one per tool call at admission, and adds a separate 60 a minute per IP. A refusal is `429 RATE_LIMITED` with `Retry-After: 60`. Details: the developer docs, "Rate limits and what a call costs".
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
## P&L since a time
|
|
496
|
+
|
|
497
|
+
`GET /api/v1/agent/keys/{prefix}/pnl?sinceMs=<unix ms>` (SDK `getProfilePnl(prefix, { sinceMs })`, MCP
|
|
498
|
+
`candle_get_profile_pnl` with `sinceMs`) adds a spot trading `window` beside the unchanged all-time
|
|
499
|
+
`pnl`. LP, Hyperliquid and creator fee income are separate from this window.
|
|
500
|
+
|
|
501
|
+
Holdings present immediately before T start at the last completed historical minute candle's close,
|
|
502
|
+
not their lifetime purchase cost. The candle containing T is excluded to avoid using future trades.
|
|
503
|
+
`boundary.marks` gives each price, `source: "candle-close"` and `updatedAt` (the candle's end, Unix ms);
|
|
504
|
+
a quiet token's last candle can be older than T. These are historical observed marks, not interpolated
|
|
505
|
+
prices at exactly T. Later buys enter at their actual trade cost. Partial sells, additional buys and
|
|
506
|
+
moves between wallets use the account engine's average-cost rules on this opening book.
|
|
507
|
+
|
|
508
|
+
`realizedGrossUsd` is sales' profit against window basis; `feesUsd` charges all in-window buy and sell
|
|
509
|
+
fees exactly once, including buys still open. Fees are debited separately and never added to cost
|
|
510
|
+
basis. Fees follow the key that placed each fill; a manual sale credits the original opener's gross
|
|
511
|
+
but its fee stays with Manual.
|
|
512
|
+
`realizedNetUsd = realizedGrossUsd - feesUsd`;
|
|
513
|
+
`unrealizedUsd` is open holdings' change against window basis; `windowedUsd` is their sum. `closed`
|
|
514
|
+
and `closedRounds` describe rounds closed during this replay, using window basis and only window
|
|
515
|
+
fees. A pre-T buy fee is excluded even when that holding is sold after T. Fills exactly at T are included.
|
|
516
|
+
|
|
517
|
+
`complete: false` and `incompleteReasons` explicitly identify missing boundary/current marks,
|
|
518
|
+
truncated history, unvalued fills or unknown/incomplete cost. In that case **`windowedUsd` is null**;
|
|
519
|
+
component figures are partial, never a complete zero. `boundary.missingMints` names missing opening
|
|
520
|
+
prices, including holdings since fully closed. Boundary coverage is checked account-wide because
|
|
521
|
+
other wallets' and unplaced holdings can fund this key's sales. Price history uses the existing
|
|
522
|
+
Candle candle store; tokens without historical candles have incomplete coverage. There are no new
|
|
523
|
+
vendor requests or fabricated opening prices. The window requires the Postgres account ledger
|
|
524
|
+
(`API_DATABASE_URL`); an unavailable ledger/history read returns an error, not an all-time fallback.
|
|
525
|
+
|
|
526
|
+
`sinceMs` must be a non-negative integer Unix timestamp in milliseconds at or before now; invalid,
|
|
527
|
+
blank, fractional or future inputs return `VALIDATION_FAILED` (400). Omitting it returns the existing
|
|
528
|
+
all-time response without `window`.
|
package/dist/index.js
CHANGED
|
@@ -189,7 +189,7 @@ function readClusterResult(value, mint) {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
// src/version.ts
|
|
192
|
-
var SERVER_VERSION = "0.10.
|
|
192
|
+
var SERVER_VERSION = "0.10.30";
|
|
193
193
|
|
|
194
194
|
// src/update-notice.ts
|
|
195
195
|
var PLAIN_VERSION = /^\d+\.\d+\.\d+$/;
|
|
@@ -1536,6 +1536,171 @@ var LAUNCH_DEFINITION = {
|
|
|
1536
1536
|
}
|
|
1537
1537
|
}
|
|
1538
1538
|
},
|
|
1539
|
+
{
|
|
1540
|
+
key: "launchRules.openDelaySecs",
|
|
1541
|
+
type: "duration",
|
|
1542
|
+
help: "Launch rules, Solana only, where the server has CURVE_LAUNCH_RULES_ENABLED on: how long after the launch buys open, at most 7 days (604800 seconds); before then only the launching wallet may buy. The CLI takes a duration (90m, 6h, 2d, or seconds); the request field is whole seconds.",
|
|
1543
|
+
wireType: "number",
|
|
1544
|
+
range: {
|
|
1545
|
+
min: "0",
|
|
1546
|
+
max: "604800",
|
|
1547
|
+
unit: "seconds"
|
|
1548
|
+
},
|
|
1549
|
+
chains: [
|
|
1550
|
+
"solana"
|
|
1551
|
+
],
|
|
1552
|
+
surfaces: {
|
|
1553
|
+
cli: {
|
|
1554
|
+
flag: "--open-in",
|
|
1555
|
+
value: "<duration>"
|
|
1556
|
+
},
|
|
1557
|
+
mcp: {
|
|
1558
|
+
key: "launchRules.openDelaySecs"
|
|
1559
|
+
},
|
|
1560
|
+
sdk: {
|
|
1561
|
+
key: "launchRules.openDelaySecs"
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
},
|
|
1565
|
+
{
|
|
1566
|
+
key: "launchRules.maxBuyPerWalletRaw",
|
|
1567
|
+
type: "decimal",
|
|
1568
|
+
help: "The most quote one wallet may spend on the curve during --cap-window, curve fee included and Candle's fee not; needs --cap-window. The CLI takes whole quote units; the request field is raw units as a decimal string.",
|
|
1569
|
+
range: {
|
|
1570
|
+
unit: "whole quote units"
|
|
1571
|
+
},
|
|
1572
|
+
chains: [
|
|
1573
|
+
"solana"
|
|
1574
|
+
],
|
|
1575
|
+
requires: [
|
|
1576
|
+
"launchRules.capWindowSecs"
|
|
1577
|
+
],
|
|
1578
|
+
surfaces: {
|
|
1579
|
+
cli: {
|
|
1580
|
+
flag: "--max-buy-per-wallet",
|
|
1581
|
+
value: "<amount>"
|
|
1582
|
+
},
|
|
1583
|
+
mcp: {
|
|
1584
|
+
key: "launchRules.maxBuyPerWalletRaw"
|
|
1585
|
+
},
|
|
1586
|
+
sdk: {
|
|
1587
|
+
key: "launchRules.maxBuyPerWalletRaw"
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
key: "launchRules.capWindowSecs",
|
|
1593
|
+
type: "duration",
|
|
1594
|
+
help: "How long the per-wallet cap holds, counted from the open: 1 second to 24 hours (86400 seconds); needs --max-buy-per-wallet.",
|
|
1595
|
+
wireType: "number",
|
|
1596
|
+
range: {
|
|
1597
|
+
min: "1",
|
|
1598
|
+
max: "86400",
|
|
1599
|
+
unit: "seconds"
|
|
1600
|
+
},
|
|
1601
|
+
chains: [
|
|
1602
|
+
"solana"
|
|
1603
|
+
],
|
|
1604
|
+
requires: [
|
|
1605
|
+
"launchRules.maxBuyPerWalletRaw"
|
|
1606
|
+
],
|
|
1607
|
+
surfaces: {
|
|
1608
|
+
cli: {
|
|
1609
|
+
flag: "--cap-window",
|
|
1610
|
+
value: "<duration>"
|
|
1611
|
+
},
|
|
1612
|
+
mcp: {
|
|
1613
|
+
key: "launchRules.capWindowSecs"
|
|
1614
|
+
},
|
|
1615
|
+
sdk: {
|
|
1616
|
+
key: "launchRules.capWindowSecs"
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
},
|
|
1620
|
+
{
|
|
1621
|
+
key: "launchRules.allowlist",
|
|
1622
|
+
type: "file",
|
|
1623
|
+
help: "Only these wallets may buy during --allowlist-window: 1 to 4,096 distinct Solana addresses; needs --allowlist-window. The CLI reads a file, one address per line or comma separated; the request field is the list.",
|
|
1624
|
+
wireType: "string[]",
|
|
1625
|
+
range: {
|
|
1626
|
+
min: "1",
|
|
1627
|
+
max: "4096",
|
|
1628
|
+
unit: "wallets"
|
|
1629
|
+
},
|
|
1630
|
+
chains: [
|
|
1631
|
+
"solana"
|
|
1632
|
+
],
|
|
1633
|
+
requires: [
|
|
1634
|
+
"launchRules.allowlistWindowSecs"
|
|
1635
|
+
],
|
|
1636
|
+
surfaces: {
|
|
1637
|
+
cli: {
|
|
1638
|
+
flag: "--allowlist",
|
|
1639
|
+
value: "<path>"
|
|
1640
|
+
},
|
|
1641
|
+
mcp: {
|
|
1642
|
+
key: "launchRules.allowlist"
|
|
1643
|
+
},
|
|
1644
|
+
sdk: {
|
|
1645
|
+
key: "launchRules.allowlist"
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
},
|
|
1649
|
+
{
|
|
1650
|
+
key: "launchRules.allowlistWindowSecs",
|
|
1651
|
+
type: "duration",
|
|
1652
|
+
help: "How long the allowlist phase holds, counted from the open: 1 second to 24 hours (86400 seconds); needs --allowlist.",
|
|
1653
|
+
wireType: "number",
|
|
1654
|
+
range: {
|
|
1655
|
+
min: "1",
|
|
1656
|
+
max: "86400",
|
|
1657
|
+
unit: "seconds"
|
|
1658
|
+
},
|
|
1659
|
+
chains: [
|
|
1660
|
+
"solana"
|
|
1661
|
+
],
|
|
1662
|
+
requires: [
|
|
1663
|
+
"launchRules.allowlist"
|
|
1664
|
+
],
|
|
1665
|
+
surfaces: {
|
|
1666
|
+
cli: {
|
|
1667
|
+
flag: "--allowlist-window",
|
|
1668
|
+
value: "<duration>"
|
|
1669
|
+
},
|
|
1670
|
+
mcp: {
|
|
1671
|
+
key: "launchRules.allowlistWindowSecs"
|
|
1672
|
+
},
|
|
1673
|
+
sdk: {
|
|
1674
|
+
key: "launchRules.allowlistWindowSecs"
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
},
|
|
1678
|
+
{
|
|
1679
|
+
key: "launchRules.devBuyMaxRaw",
|
|
1680
|
+
type: "decimal",
|
|
1681
|
+
help: "The most the launching wallet may buy before the open, only with --open-in: at least the launch's own buy, at most the dev-buy ceiling, and the buy by default. That spend counts toward the per-wallet cap after the open. The CLI takes whole quote units; the request field is raw units as a decimal string.",
|
|
1682
|
+
range: {
|
|
1683
|
+
unit: "whole quote units"
|
|
1684
|
+
},
|
|
1685
|
+
chains: [
|
|
1686
|
+
"solana"
|
|
1687
|
+
],
|
|
1688
|
+
requires: [
|
|
1689
|
+
"launchRules.openDelaySecs"
|
|
1690
|
+
],
|
|
1691
|
+
surfaces: {
|
|
1692
|
+
cli: {
|
|
1693
|
+
flag: "--dev-buy-max",
|
|
1694
|
+
value: "<amount>"
|
|
1695
|
+
},
|
|
1696
|
+
mcp: {
|
|
1697
|
+
key: "launchRules.devBuyMaxRaw"
|
|
1698
|
+
},
|
|
1699
|
+
sdk: {
|
|
1700
|
+
key: "launchRules.devBuyMaxRaw"
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
},
|
|
1539
1704
|
{
|
|
1540
1705
|
key: "dryRun",
|
|
1541
1706
|
type: "boolean",
|
|
@@ -1558,7 +1723,7 @@ var LAUNCH_DEFINITION = {
|
|
|
1558
1723
|
|
|
1559
1724
|
// src/launch-schema.ts
|
|
1560
1725
|
var M2_TOP_KEYS = new Set(["wallet", "buy", "feeWallet", "feeWalletMultisig"]);
|
|
1561
|
-
var GROUPED_TOP_KEYS = new Set(["socials", "curve", "poolFee"]);
|
|
1726
|
+
var GROUPED_TOP_KEYS = new Set(["socials", "curve", "poolFee", "launchRules"]);
|
|
1562
1727
|
function mcpKey(field) {
|
|
1563
1728
|
const surface = field.surfaces.mcp;
|
|
1564
1729
|
return "key" in surface ? surface.key : undefined;
|
|
@@ -1683,6 +1848,20 @@ function poolFeeSchema() {
|
|
|
1683
1848
|
}).strict()
|
|
1684
1849
|
]).optional().describe([tierHelp, startHelp, endHelp, minutesHelp].join(" "));
|
|
1685
1850
|
}
|
|
1851
|
+
function launchRulesSchema() {
|
|
1852
|
+
const helps = [];
|
|
1853
|
+
const shape = {};
|
|
1854
|
+
for (const field of mcpLaunchFields()) {
|
|
1855
|
+
const key = mcpKey(field);
|
|
1856
|
+
if (!key?.startsWith("launchRules."))
|
|
1857
|
+
continue;
|
|
1858
|
+
const wire = "wireType" in field ? field.wireType : undefined;
|
|
1859
|
+
const schema = wire === "number" ? z3.number().int() : wire === "string[]" ? z3.array(z3.string()) : z3.string();
|
|
1860
|
+
shape[key.slice("launchRules.".length)] = schema.optional().describe(field.help);
|
|
1861
|
+
helps.push(field.help);
|
|
1862
|
+
}
|
|
1863
|
+
return z3.object(shape).passthrough().optional().describe(helps.join(" "));
|
|
1864
|
+
}
|
|
1686
1865
|
function scalarSchema(field) {
|
|
1687
1866
|
const values = "values" in field ? field.values : undefined;
|
|
1688
1867
|
let schema;
|
|
@@ -1714,6 +1893,7 @@ function buildLaunchTokenShape() {
|
|
|
1714
1893
|
shape.socials = socialsSchema();
|
|
1715
1894
|
shape.curve = curveSchema();
|
|
1716
1895
|
shape.poolFee = poolFeeSchema();
|
|
1896
|
+
shape.launchRules = launchRulesSchema();
|
|
1717
1897
|
shape.buyAmount = z3.union([z3.string(), z3.number()]).optional().describe(BUY_AMOUNT_DESCRIPTION);
|
|
1718
1898
|
return shape;
|
|
1719
1899
|
}
|
|
@@ -2219,6 +2399,7 @@ async function resolveToken(args, cfg, doFetch) {
|
|
|
2219
2399
|
return read.err;
|
|
2220
2400
|
return { text: JSON.stringify({ success: true, chain, mint: args.mint, market: read.market }, null, 2) };
|
|
2221
2401
|
}
|
|
2402
|
+
var STOP_CLEARED_BY = "An account owner reviews the profile and resumes it from the agent console; an API key cannot resume it.";
|
|
2222
2403
|
async function executionStatus(cfg, doFetch) {
|
|
2223
2404
|
const apiKey = requireApiKey(cfg);
|
|
2224
2405
|
const get = async (path) => {
|
|
@@ -2233,27 +2414,33 @@ async function executionStatus(cfg, doFetch) {
|
|
|
2233
2414
|
}
|
|
2234
2415
|
return { ok: res.ok, status: res.status, body };
|
|
2235
2416
|
};
|
|
2236
|
-
const [wallets, tier, limits] = await Promise.all([
|
|
2417
|
+
const [wallets, tier, limits, risk] = await Promise.all([
|
|
2237
2418
|
get("/api/v1/agent/wallets/embedded"),
|
|
2238
2419
|
get("/api/v1/agent/tier"),
|
|
2239
|
-
get("/api/v1/agent/keys/self/limits")
|
|
2420
|
+
get("/api/v1/agent/keys/self/limits"),
|
|
2421
|
+
get("/api/v1/agent/keys/self/risk")
|
|
2240
2422
|
]);
|
|
2241
2423
|
const unreadable = [
|
|
2242
2424
|
["wallets", wallets],
|
|
2243
2425
|
["tier", tier],
|
|
2244
|
-
["limits", limits]
|
|
2426
|
+
["limits", limits],
|
|
2427
|
+
["risk", risk]
|
|
2245
2428
|
].filter(([, r]) => !r.ok);
|
|
2246
2429
|
const tierBody = tier.ok ? tier.body : undefined;
|
|
2430
|
+
const riskBody = risk.ok ? risk.body : undefined;
|
|
2431
|
+
const stopped = riskBody?.stopped === true;
|
|
2247
2432
|
return {
|
|
2248
2433
|
text: JSON.stringify({
|
|
2249
2434
|
success: true,
|
|
2250
|
-
ready: unreadable.length === 0 ? true : undefined,
|
|
2435
|
+
ready: stopped ? false : unreadable.length === 0 ? true : undefined,
|
|
2436
|
+
stopped: stopped ? { tripped: riskBody?.tripped ?? null, clearedBy: STOP_CLEARED_BY } : undefined,
|
|
2251
2437
|
notice: tierBody?.maxExpired ? tierBody.maxExpiredNotice : undefined,
|
|
2252
2438
|
updateAvailable: updateAvailable() ?? undefined,
|
|
2253
2439
|
unreadable: unreadable.length > 0 ? unreadable.map(([name]) => name) : undefined,
|
|
2254
2440
|
wallets: wallets.body,
|
|
2255
2441
|
tier: tier.body,
|
|
2256
|
-
limits: limits.body
|
|
2442
|
+
limits: limits.body,
|
|
2443
|
+
risk: risk.body
|
|
2257
2444
|
}, null, 2),
|
|
2258
2445
|
...unreadable.length > 0 ? { isError: true } : {}
|
|
2259
2446
|
};
|
|
@@ -6306,6 +6493,7 @@ var TOOL_NAMES = [
|
|
|
6306
6493
|
"candle_vamp_candidates",
|
|
6307
6494
|
"candle_token_events",
|
|
6308
6495
|
"candle_token_card",
|
|
6496
|
+
"candle_get_price_history",
|
|
6309
6497
|
"candle_token_edit",
|
|
6310
6498
|
"candle_token_history",
|
|
6311
6499
|
"candle_token_page_about",
|
|
@@ -6347,6 +6535,9 @@ var TOOL_NAMES = [
|
|
|
6347
6535
|
"candle_set_profile_wallets",
|
|
6348
6536
|
"candle_get_profile_pnl",
|
|
6349
6537
|
"candle_get_profile_trades",
|
|
6538
|
+
"candle_get_paper_inventory",
|
|
6539
|
+
"candle_get_profile_paper",
|
|
6540
|
+
"candle_get_account_pnl",
|
|
6350
6541
|
"candle_get_portfolio",
|
|
6351
6542
|
"candle_resolve_token",
|
|
6352
6543
|
"candle_execution_status",
|
|
@@ -6492,6 +6683,21 @@ function buildRequest(name, args, cfg) {
|
|
|
6492
6683
|
init: { method: "GET", headers: jsonHeaders() }
|
|
6493
6684
|
};
|
|
6494
6685
|
}
|
|
6686
|
+
case "candle_get_price_history": {
|
|
6687
|
+
const { chain, mint, interval, limit, to } = args;
|
|
6688
|
+
const query2 = new URLSearchParams;
|
|
6689
|
+
if (interval !== undefined)
|
|
6690
|
+
query2.set("interval", interval);
|
|
6691
|
+
if (limit !== undefined)
|
|
6692
|
+
query2.set("limit", String(limit));
|
|
6693
|
+
if (to !== undefined)
|
|
6694
|
+
query2.set("to", String(to));
|
|
6695
|
+
const qs = query2.size > 0 ? `?${query2}` : "";
|
|
6696
|
+
return {
|
|
6697
|
+
url: `${base2}/api/v1/markets/${encodeURIComponent(chain ?? "solana")}/${encodeURIComponent(mint)}/candles${qs}`,
|
|
6698
|
+
init: { method: "GET", headers: jsonHeaders() }
|
|
6699
|
+
};
|
|
6700
|
+
}
|
|
6495
6701
|
case "candle_token_events": {
|
|
6496
6702
|
const { chain, mint, since, limit, before } = args;
|
|
6497
6703
|
const params = new URLSearchParams;
|
|
@@ -6649,7 +6855,8 @@ function buildRequest(name, args, cfg) {
|
|
|
6649
6855
|
case "candle_get_operation": {
|
|
6650
6856
|
const apiKey = requireApiKey2(cfg);
|
|
6651
6857
|
const { clientId: clientId2, kind } = args;
|
|
6652
|
-
const
|
|
6858
|
+
const id = encodeURIComponent(clientId2);
|
|
6859
|
+
const path = kind === "launch" ? `/api/v1/launch/headless/jobs/${id}` : kind === "swap" ? `/api/v1/agent/swap/by-client-id/${id}` : `/api/v1/trade/agent/jobs/${id}`;
|
|
6653
6860
|
return { url: `${base2}${path}`, init: { method: "GET", headers: jsonHeaders(apiKey) } };
|
|
6654
6861
|
}
|
|
6655
6862
|
case "candle_get_staking": {
|
|
@@ -6724,9 +6931,10 @@ function buildRequest(name, args, cfg) {
|
|
|
6724
6931
|
}
|
|
6725
6932
|
case "candle_get_profile_pnl": {
|
|
6726
6933
|
const apiKey = requireApiKey2(cfg);
|
|
6727
|
-
const { keyPrefix } = args;
|
|
6934
|
+
const { keyPrefix, sinceMs } = args;
|
|
6935
|
+
const query2 = sinceMs !== undefined ? `?sinceMs=${encodeURIComponent(String(sinceMs))}` : "";
|
|
6728
6936
|
return {
|
|
6729
|
-
url: `${base2}/api/v1/agent/keys/${encodeURIComponent(keyPrefix)}/pnl`,
|
|
6937
|
+
url: `${base2}/api/v1/agent/keys/${encodeURIComponent(keyPrefix)}/pnl${query2}`,
|
|
6730
6938
|
init: { method: "GET", headers: jsonHeaders(apiKey) }
|
|
6731
6939
|
};
|
|
6732
6940
|
}
|
|
@@ -6739,13 +6947,40 @@ function buildRequest(name, args, cfg) {
|
|
|
6739
6947
|
}
|
|
6740
6948
|
case "candle_get_profile_trades": {
|
|
6741
6949
|
const apiKey = requireApiKey2(cfg);
|
|
6742
|
-
const { keyPrefix, limit } = args;
|
|
6743
|
-
const
|
|
6950
|
+
const { keyPrefix, limit, since, until, cursor } = args;
|
|
6951
|
+
const params = new URLSearchParams;
|
|
6952
|
+
if (limit !== undefined)
|
|
6953
|
+
params.set("limit", String(limit));
|
|
6954
|
+
if (since !== undefined)
|
|
6955
|
+
params.set("since", since);
|
|
6956
|
+
if (until !== undefined)
|
|
6957
|
+
params.set("until", until);
|
|
6958
|
+
if (cursor !== undefined)
|
|
6959
|
+
params.set("cursor", cursor);
|
|
6744
6960
|
return {
|
|
6745
|
-
url: `${base2}/api/v1/agent/keys/${encodeURIComponent(keyPrefix)}/trades${
|
|
6961
|
+
url: `${base2}/api/v1/agent/keys/${encodeURIComponent(keyPrefix)}/trades${params.size ? `?${params}` : ""}`,
|
|
6746
6962
|
init: { method: "GET", headers: jsonHeaders(apiKey) }
|
|
6747
6963
|
};
|
|
6748
6964
|
}
|
|
6965
|
+
case "candle_get_paper_inventory": {
|
|
6966
|
+
return {
|
|
6967
|
+
url: `${base2}/api/v1/trade/agent/paper/inventory`,
|
|
6968
|
+
init: { method: "GET", headers: jsonHeaders(requireApiKey2(cfg)) }
|
|
6969
|
+
};
|
|
6970
|
+
}
|
|
6971
|
+
case "candle_get_profile_paper": {
|
|
6972
|
+
const { keyPrefix } = args;
|
|
6973
|
+
return {
|
|
6974
|
+
url: `${base2}/api/v1/agent/keys/${encodeURIComponent(keyPrefix)}/paper`,
|
|
6975
|
+
init: { method: "GET", headers: jsonHeaders(requireApiKey2(cfg)) }
|
|
6976
|
+
};
|
|
6977
|
+
}
|
|
6978
|
+
case "candle_get_account_pnl": {
|
|
6979
|
+
return {
|
|
6980
|
+
url: `${base2}/api/v1/agent/keys/pnl`,
|
|
6981
|
+
init: { method: "GET", headers: jsonHeaders(requireApiKey2(cfg)) }
|
|
6982
|
+
};
|
|
6983
|
+
}
|
|
6749
6984
|
case "candle_transfer": {
|
|
6750
6985
|
const apiKey = requireApiKey2(cfg);
|
|
6751
6986
|
return {
|
|
@@ -6781,10 +7016,10 @@ async function callAndRelay(name, args, cfg, doFetch) {
|
|
|
6781
7016
|
...res.ok ? {} : { isError: true }
|
|
6782
7017
|
};
|
|
6783
7018
|
}
|
|
6784
|
-
var OPERATION_NOT_FOUND_RULE = "No record exists under this id in the ledger `kind` asked. That does not prove the write did " + "not run: a write that is still starting has no record yet. If a write was sent with this id, " + "send the SAME request again with the SAME id. It returns the stored outcome, or reports the one " + "still running. It never executes a second time. A refusal of that resend (a conflict or a " + "limit) is an answer about the resend, not about the write: read candle_get_operation again " + "before reporting an outcome. Never send it under a new id: a new id is a second trade or " + "launch. Check `kind`:
|
|
7019
|
+
var OPERATION_NOT_FOUND_RULE = "No record exists under this id in the ledger `kind` asked. That does not prove the write did " + "not run: a write that is still starting has no record yet. If a write was sent with this id, " + "send the SAME request again with the SAME id. It returns the stored outcome, or reports the one " + "still running. It never executes a second time. A refusal of that resend (a conflict or a " + "limit) is an answer about the resend, not about the write: read candle_get_operation again " + "before reporting an outcome. Never send it under a new id: a new id is a second trade, swap or " + "launch. Check `kind`: an id read under the wrong kind has no record there. A swap sent without " + "a `clientSwapId` has no record at all.";
|
|
6785
7020
|
function operationNotFoundNext(clientId2, kind) {
|
|
6786
|
-
const
|
|
6787
|
-
return `No ${kind} record under id "${clientId2}" yet. That does not prove the write did not run: a ` + "write that is still starting has no record yet. If a write was sent with this id, send the " + `SAME request again with the SAME id ("${clientId2}"). It returns the stored outcome, or reports ` + "the one still running. It never executes a second time. A refusal of that resend (a conflict " + "or a limit) is an answer about the resend, not about the write: read candle_get_operation " + `again before reporting an outcome. Never send it under a new id: a new id is a second ${kind}. ` + `This read asked kind "${kind}": a ${
|
|
7021
|
+
const others = ["trade", "swap", "launch"].filter((k) => k !== kind).join(" or ");
|
|
7022
|
+
return `No ${kind} record under id "${clientId2}" yet. That does not prove the write did not run: a ` + "write that is still starting has no record yet. If a write was sent with this id, send the " + `SAME request again with the SAME id ("${clientId2}"). It returns the stored outcome, or reports ` + "the one still running. It never executes a second time. A refusal of that resend (a conflict " + "or a limit) is an answer about the resend, not about the write: read candle_get_operation " + `again before reporting an outcome. Never send it under a new id: a new id is a second ${kind}. ` + `This read asked kind "${kind}": a ${others} id has no record there.`;
|
|
6788
7023
|
}
|
|
6789
7024
|
async function getOperation(args, cfg, doFetch) {
|
|
6790
7025
|
const { url, init } = buildRequest("candle_get_operation", args, cfg);
|
|
@@ -6799,7 +7034,7 @@ async function getOperation(args, cfg, doFetch) {
|
|
|
6799
7034
|
const code = body?.error?.code;
|
|
6800
7035
|
if (code === "JOB_NOT_FOUND") {
|
|
6801
7036
|
const { clientId: clientId2, kind } = args;
|
|
6802
|
-
const next = operationNotFoundNext(clientId2, kind === "launch"
|
|
7037
|
+
const next = operationNotFoundNext(clientId2, kind === "launch" || kind === "swap" ? kind : "trade");
|
|
6803
7038
|
text = JSON.stringify({ ...body, outcome: "unknown", next });
|
|
6804
7039
|
}
|
|
6805
7040
|
}
|
|
@@ -6834,6 +7069,13 @@ var getMarketShape = {
|
|
|
6834
7069
|
chain: z9.string().describe('"solana" or "hood"'),
|
|
6835
7070
|
mint: z9.string().describe("Token mint (solana) or contract address (hood)")
|
|
6836
7071
|
};
|
|
7072
|
+
var priceHistoryShape = {
|
|
7073
|
+
chain: z9.string().optional().describe('"solana" (the default). Hood is refused with the REST route that serves it'),
|
|
7074
|
+
mint: z9.string().describe("Token mint"),
|
|
7075
|
+
interval: z9.enum(["1m", "5m", "15m", "1h", "4h", "1d"]).optional().describe("Bar length. Default 1h"),
|
|
7076
|
+
limit: z9.number().int().min(1).max(500).optional().describe("How many bars, 1-500. Default 100"),
|
|
7077
|
+
to: z9.number().int().positive().optional().describe("Unix seconds: the newest bar opens at or before this. Default now; page back with the oldest bar's time")
|
|
7078
|
+
};
|
|
6837
7079
|
var TOKEN_EVENTS_LIMIT_MESSAGE = "limit must be a whole number from 1 to 1000";
|
|
6838
7080
|
var tokenEventsLimit = z9.preprocess((value) => {
|
|
6839
7081
|
if (typeof value !== "string")
|
|
@@ -6912,8 +7154,8 @@ var getAgentPositionsShape = {
|
|
|
6912
7154
|
kind: z9.enum(["spot", "perp"]).optional().describe("Only spot tokens or only Hyperliquid perps; omit for both")
|
|
6913
7155
|
};
|
|
6914
7156
|
var getOperationShape = {
|
|
6915
|
-
clientId: z9.string().describe("The clientTradeId or clientLaunchId the write used, or that the tool echoed back to you"),
|
|
6916
|
-
kind: z9.enum(["trade", "launch"]).describe("Which rail the id belongs to. Required: ids are caller-chosen strings with no shape to dispatch on")
|
|
7157
|
+
clientId: z9.string().describe("The clientTradeId, clientSwapId or clientLaunchId the write used, or that the tool echoed back to you"),
|
|
7158
|
+
kind: z9.enum(["trade", "swap", "launch"]).describe("Which rail the id belongs to. Required: ids are caller-chosen strings with no shape to dispatch on")
|
|
6917
7159
|
};
|
|
6918
7160
|
var vampCandidatesShape = {
|
|
6919
7161
|
limit: z9.number().optional().describe("Entries to return, 1 to 50 (default 20)"),
|
|
@@ -6951,12 +7193,17 @@ var profileWalletsShape = {
|
|
|
6951
7193
|
keyPrefix: z9.string().describe("The profile's API key prefix, as listed by candle keys list or in the dashboard")
|
|
6952
7194
|
};
|
|
6953
7195
|
var profilePnlShape = {
|
|
6954
|
-
keyPrefix: z9.string().describe("The profile's API key prefix")
|
|
7196
|
+
keyPrefix: z9.string().describe("The profile's API key prefix"),
|
|
7197
|
+
sinceMs: z9.number().int().nonnegative().optional().describe("Unix time in milliseconds. Adds a `window` block: realized and open mark changes since then, including older holdings. Buy and sell fees are charged once. Read complete and incompleteReasons; windowedUsd is null when incomplete. The all-time `pnl` is unchanged.")
|
|
6955
7198
|
};
|
|
6956
7199
|
var profileTradesShape = {
|
|
6957
7200
|
keyPrefix: z9.string().describe("The profile's API key prefix"),
|
|
6958
|
-
limit: z9.number().optional().describe("How many of the most recent trades to return. Default 200, max 1000.")
|
|
7201
|
+
limit: z9.number().optional().describe("How many of the most recent trades to return. Default 200, max 1000."),
|
|
7202
|
+
since: z9.string().optional().describe("Inclusive lower bound on the trade's createdAt: an ISO 8601 time or epoch milliseconds."),
|
|
7203
|
+
until: z9.string().optional().describe("Exclusive upper bound on createdAt, in the same forms as `since`."),
|
|
7204
|
+
cursor: z9.string().optional().describe("The `nextCursor` of the previous page, to continue the same window.")
|
|
6959
7205
|
};
|
|
7206
|
+
var noArgsShape = {};
|
|
6960
7207
|
var setProfileWalletsShape = {
|
|
6961
7208
|
keyPrefix: z9.string().describe("The profile's API key prefix"),
|
|
6962
7209
|
walletIds: z9.array(z9.string()).describe("The linked-wallet ids the profile may spend from -- ids, not addresses. This REPLACES the " + "whole set: any wallet omitted loses access. An empty array assigns none.")
|
|
@@ -6974,7 +7221,7 @@ var tradeShape = {
|
|
|
6974
7221
|
side: z9.enum(["buy", "sell"]),
|
|
6975
7222
|
amount: z9.string().optional().describe("Decimal amount. Buys: how much of THIS TOKEN'S OWN quote asset to spend (SOL for a " + 'SOL-launched token, USDC for a USDC-quoted one, and so on: e.g. "0.5"). Sells: how many ' + "TOKENS to sell. Pass exactly one of amount or percent."),
|
|
6976
7223
|
percent: z9.number().optional().describe("Sells only: sell this percent (integer 1-100) of the holding. Live trades size against the " + "embedded wallet. Paper trades (`paper: true`) size against this key's paper inventory -- " + "the position a previous paper buy credited -- because paper never moves the live wallet."),
|
|
6977
|
-
quoteAsset: z9.string().optional().describe('What the wallet spends on a buy or receives on a sell: "sol", "usdc" or "cndl" on Solana, ' + '"eth" or "usdg" on Hood. Echoing the `quoteAsset` a quote (POST /api/v1/trade/agent/quote) returned is safe. On Solana it applies only ' + "to an arbitrary mint Candle never launched (a buy
|
|
7224
|
+
quoteAsset: z9.string().optional().describe('What the wallet spends on a buy or receives on a sell: "sol", "usdc" or "cndl" on Solana, ' + '"eth" or "usdg" on Hood. Echoing the `quoteAsset` a quote (POST /api/v1/trade/agent/quote) returned is safe. On Solana it applies only ' + "to an arbitrary mint Candle never launched (a buy or sell works on any plan) and is ignored for a Candle token, " + "whose quote comes from the token itself. On Hood it is the settlement asset of a DEX " + "trade; a USDG buy adds an approval transaction an ETH buy does not. It is not the route: " + "the cheapest path to the asset is chosen separately. Defaults to sol / ETH settlement."),
|
|
6978
7225
|
maxSlippageBps: z9.number().int().min(0).max(5000).optional().describe("Slippage bound in bps, 0-5000 (50%). Server defaults to 100 (1%)"),
|
|
6979
7226
|
clientTradeId: z9.string().optional().describe("Idempotency key. Auto-generated when omitted and echoed in the result. Retrying with the " + "SAME id is safe (idempotent replay); a new id is a SECOND trade."),
|
|
6980
7227
|
paper: z9.preprocess((value) => value === "true" || value === 1 || value === "1" ? true : value === "false" || value === 0 || value === "0" ? false : value, z9.boolean().optional().describe("Rehearse instead of trading. The request passes every admission rule a live trade passes " + "-- the same planner, spend gate, key cap and loss limits -- and records the quote, but " + "nothing is ever broadcast and no funds move. Use it to check that a strategy is admitted " + "before risking anything on it. A paper fill is optimistic by construction: it books the " + "quoted price, so the gap between a paper arm and a live one IS the execution cost. " + "A sell of a mint this key already paper-bought also closes that paper book when the " + "live wallet is empty, even if this flag is omitted."))
|
|
@@ -7160,9 +7407,7 @@ function registerToolSubset(server, options) {
|
|
|
7160
7407
|
}, async (args) => callAndRelay("candle_get_market", args, getConfig(), doFetch));
|
|
7161
7408
|
register("candle_token_forensics", {
|
|
7162
7409
|
title: "Token forensics",
|
|
7163
|
-
description:
|
|
7164
|
-
|
|
7165
|
-
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.`,
|
|
7410
|
+
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. deployer.priorLaunches is counted (pump.fun curves for a curve creator; record.count says the source); deployer.launcher is the wallet that sent the launch, often a launch tool's signer, never the developer; launch.deployerLaunches is that wallet's inclusive informational count, never a warning. Every measurement carries a coverage note -- 'unavailable' is not 'clean'. No key needed.\n\nMARKET_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.",
|
|
7166
7411
|
inputSchema: tokenForensicsShape
|
|
7167
7412
|
}, async (args) => callAndRelay("candle_token_forensics", args, getConfig(), doFetch));
|
|
7168
7413
|
register("candle_token_clusters", {
|
|
@@ -7197,6 +7442,11 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
|
|
|
7197
7442
|
inputSchema: tokenHistoryShape,
|
|
7198
7443
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
7199
7444
|
}, async (args) => callAndRelay("candle_token_history", args, getConfig(), doFetch));
|
|
7445
|
+
register("candle_get_price_history", {
|
|
7446
|
+
title: "Get price history",
|
|
7447
|
+
description: "One Solana token's price history: OHLC bars in USD per token, oldest first, the same bars as the token's chart on Candle. interval 1m, 5m, 15m, 1h, 4h or 1d (default 1h); limit 1-500 (default 100); to is unix seconds (default now). The answer is up to limit bars at or before to (limit is a maximum). Coverage is bounded by source: Candle's own bars come from the latest 1,500 active minutes and a bar whose start cannot be established is left out; the indexer is searched only (limit + 1) x interval seconds back from to. A quiet token can therefore return fewer bars than limit, and a minute with no trades has no bar, so read each bar's time rather than assuming a gapless series. source says which answered: candle (Candle's own curve bars, volumeUnit usd) or birdeye (the indexer's, volumeUnit token). An empty candles list means no bars in the searched coverage, not no history and not a zero price; page back with to. No key needed and none is sent. Hood is refused (400 VALIDATION_FAILED naming GET /api/v1/hood/token/:address/candles). For the current price and 5m/1h/24h change, read candle_get_market.",
|
|
7448
|
+
inputSchema: priceHistoryShape
|
|
7449
|
+
}, async (args) => callAndRelay("candle_get_price_history", args, getConfig(), doFetch));
|
|
7200
7450
|
register("candle_token_events", {
|
|
7201
7451
|
title: "Token events",
|
|
7202
7452
|
description: `One token's event list, newest first: every signal (pattern and summary, new_ticker included), every callout, and every tracked-wallet trade (buys and sells, W or L on a sell whose outcome is known), plus trades Candle's own agents and users made. Each wallet carries its display name or short address, its kind (dev, kol, smart, agent, tracked) and its public group names. No key needed; none is sent even when one is configured, so group names follow the public tier. Off on a deployment until it switches TOKEN_EVENTS on: off, the call answers 503 TOKEN_EVENTS_DISABLED (retryable: false) and that is the server's answer, not a broken tool.
|
|
@@ -7294,13 +7544,13 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
|
|
|
7294
7544
|
});
|
|
7295
7545
|
register("candle_get_operation", {
|
|
7296
7546
|
title: "What happened to a write",
|
|
7297
|
-
description: "Look up a trade or launch by the id its write used, and find out whether it landed. " + `Reads only; moves nothing.
|
|
7547
|
+
description: "Look up a trade, swap or launch by the id its write used, and find out whether it landed. " + `Reads only; moves nothing.
|
|
7298
7548
|
|
|
7299
7549
|
` + "Call this after a timeout, after a restart, or any time you hold an id and do not know " + `the outcome. Do NOT re-send the write to find out.
|
|
7300
7550
|
|
|
7301
|
-
` + `
|
|
7551
|
+
` + `The answers, and the next move for each:
|
|
7302
7552
|
` + "- `confirmed`/`failed`: it is over. `signature` proves the first, `errorCode` explains " + `the second.
|
|
7303
|
-
` + "- `built`/`submitted`: still in flight. Wait and ask again.\n" + `- 404 (\`JOB_NOT_FOUND\`): unknown, not an outcome. ${OPERATION_NOT_FOUND_RULE} The result ` + 'says the same in `outcome: "unknown"` and `next`.\n\n' + "Amounts come back RAW, deliberately: the answer you need after a timeout is the outcome, " + "and you already know what you asked for.",
|
|
7553
|
+
` + "- `built`/`submitted`: still in flight. Wait and ask again.\n" + "- kind `swap` (candle_swap's `clientSwapId`) answers `job.outcome` instead: `in_flight` " + "(ask again), `confirmed` (`job.hashes`), `failed` (refused before any broadcast; the SAME " + "request with the SAME id starts over), `partial` (`hashes[0]` landed, " + "a later leg did not) or `indeterminate` (broadcast, outcome unknown; check " + "`error.message`). `partial` and `indeterminate` are final for that id.\n" + `- 404 (\`JOB_NOT_FOUND\`): unknown, not an outcome. ${OPERATION_NOT_FOUND_RULE} The result ` + 'says the same in `outcome: "unknown"` and `next`.\n\n' + "Amounts come back RAW, deliberately: the answer you need after a timeout is the outcome, " + "and you already know what you asked for.",
|
|
7304
7554
|
inputSchema: getOperationShape
|
|
7305
7555
|
}, async (args) => getOperation(args, getConfig(), doFetch));
|
|
7306
7556
|
register("candle_get_staking", {
|
|
@@ -7357,14 +7607,29 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
|
|
|
7357
7607
|
}, async (args) => callAndRelay("candle_set_profile_wallets", args, getConfig(), doFetch));
|
|
7358
7608
|
register("candle_get_profile_pnl", {
|
|
7359
7609
|
title: "Read an agent profile's P&L",
|
|
7360
|
-
description: "Optional `board` beside `pnl` carries profileId, creatorFeesUsd, feeClaimsUnvalued, feeClaimsPartial and " + "totalPnlUsd (absent before a board entry exists): the all-time public board total, including fee claims " + "at their value when claimed, credited to the agent that launched the token when the claiming account " + "is the launching account. `feesUsd` is fees paid; `creatorFeesUsd` is fees earned. Check feeClaimsPartial " + "and feeClaimsUnvalued. `board` is absent on an older backend or a failed board read; it does not change " + "ledger pnl.totalUsd. Staking rewards are held at zero cost; claim value is informational, never added again. " + "This profile's share of the account's P&L: realized profit on the sales it made and on positions its buy opened that closed through a sale made by hand in the web app or an exit outside Candle (the fee and trade count stay under Manual), 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, plus the Hyperliquid net when `perpsInTotal` is true. 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`. " + PROFILE_PNL_HYPERLIQUID + " Reads only.",
|
|
7610
|
+
description: "Optional `board` beside `pnl` carries profileId, creatorFeesUsd, feeClaimsUnvalued, feeClaimsPartial and " + "totalPnlUsd (absent before a board entry exists): the all-time public board total, including fee claims " + "at their value when claimed, credited to the agent that launched the token when the claiming account " + "is the launching account. `feesUsd` is fees paid; `creatorFeesUsd` is fees earned. Check feeClaimsPartial " + "and feeClaimsUnvalued. `board` is absent on an older backend or a failed board read; it does not change " + "ledger pnl.totalUsd. Staking rewards are held at zero cost; claim value is informational, never added again. " + "This profile's share of the account's P&L: realized profit on the sales it made and on positions its buy opened that closed through a sale made by hand in the web app or an exit outside Candle (the fee and trade count stay under Manual), 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, plus the Hyperliquid net when `perpsInTotal` is true. 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`. " + PROFILE_PNL_HYPERLIQUID + " Pass `sinceMs` for a `window` block beside all-time `pnl`: existing holdings start at the last " + "completed historical minute candle's close, returned in `boundary.marks` with its timestamp. " + "Sales and open positions use that window basis, with later buys at trade cost. `feesUsd` includes " + "both buy and sell fees once, separately from basis. `windowedUsd` is " + "realizedNetUsd plus unrealizedUsd. Missing boundary/current marks or incomplete history return " + "complete:false, incompleteReasons and windowedUsd:null; component sums are partial then. " + "This is spot trading P&L; LP, perps and fee income remain separate. Reads only.",
|
|
7361
7611
|
inputSchema: profilePnlShape
|
|
7362
7612
|
}, async (args) => callAndRelay("candle_get_profile_pnl", args, getConfig(), doFetch));
|
|
7363
7613
|
register("candle_get_profile_trades", {
|
|
7364
7614
|
title: "Read an agent profile's trade history",
|
|
7365
|
-
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.",
|
|
7615
|
+
description: "Orders, actual fills, fees, timestamps and transaction hashes for this profile. Page by time with " + "`since`, `until` and `cursor` (continue with the response's `nextCursor`). 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.",
|
|
7366
7616
|
inputSchema: profileTradesShape
|
|
7367
7617
|
}, async (args) => callAndRelay("candle_get_profile_trades", args, getConfig(), doFetch));
|
|
7618
|
+
register("candle_get_paper_inventory", {
|
|
7619
|
+
title: "Read this key's paper inventory",
|
|
7620
|
+
description: "This key's PAPER book in raw units: the positions that paper buys have credited and a paper " + "percent sell sizes itself from. Derived from admitted quotes; no money moved and the live " + "wallet is untouched. Never mix it with live holdings (candle_get_portfolio). Fails retryably " + "when the book cannot be read completely. Needs swap:write on the key. Reads only.",
|
|
7621
|
+
inputSchema: noArgsShape
|
|
7622
|
+
}, async (args) => callAndRelay("candle_get_paper_inventory", args, getConfig(), doFetch));
|
|
7623
|
+
register("candle_get_profile_paper", {
|
|
7624
|
+
title: "Read an agent profile's paper P&L",
|
|
7625
|
+
description: "One profile's PAPER results: the same accounting as candle_get_profile_pnl, from trades that " + "never happened. `pnl` is the paper P&L and `trades` its 50 most recent paper trades. Carries " + '`basis: "quote"`: the figures come from quotes at admission and are optimistic by the ' + "slippage and fees real execution pays. Never add them to live P&L. Reads only.",
|
|
7626
|
+
inputSchema: profilePnlShape
|
|
7627
|
+
}, async (args) => callAndRelay("candle_get_profile_paper", args, getConfig(), doFetch));
|
|
7628
|
+
register("candle_get_account_pnl", {
|
|
7629
|
+
title: "Read P&L across all of an account's agent keys",
|
|
7630
|
+
description: "The account's P&L split by agent profile in one read, as `candle pnl` without `--profile` " + "does: one row per profile with its ledger figures and, where it has a board entry, `board` " + "(all-time public total with creator-fee claims credited to the launching agent, so a profile's " + "board total can differ from its per-key ledger P&L). A wallet shared by several keys books a " + "round to its bound key, else the key whose buy opened it. Use candle_get_profile_pnl for one " + "profile's positions. Reads only.",
|
|
7631
|
+
inputSchema: noArgsShape
|
|
7632
|
+
}, async (args) => callAndRelay("candle_get_account_pnl", args, getConfig(), doFetch));
|
|
7368
7633
|
register("candle_get_portfolio", {
|
|
7369
7634
|
title: "Read what the account holds, on Solana and Hood",
|
|
7370
7635
|
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." + (options.localHyperliquid ? " This local server adds `hyperliquid`: each Hood TEE wallet's Hyperliquid account (account value, " + "unrealized P&L, positions, risk level at the default thresholds) read live from Hyperliquid, with a " + "subtotal. It is not in the ledger and is never part of any total. `read: false` with a reason, or " + "entries in `unread`, means those accounts are unknown, not empty." : ""),
|
|
@@ -7386,7 +7651,7 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
|
|
|
7386
7651
|
});
|
|
7387
7652
|
register("candle_execution_status", {
|
|
7388
7653
|
title: "Can this key execute right now",
|
|
7389
|
-
description: "One call before trading: the embedded wallets to spend from, the tier that decides what " + "may be traded,
|
|
7654
|
+
description: "One call before trading: the embedded wallets to spend from, the tier that decides what " + "may be traded, this key's own spend limits, and its loss limits and stop state (risk: " + "lossLimits, state, stopped, tripped). A stopped key reports ready: false and a top-level " + "stopped naming the bound that broke; every trade answers RISK_LIMIT_REACHED until an " + "account owner resumes it, so stop trading and say why rather than retrying. Any scope " + "can read its own. 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.",
|
|
7390
7655
|
inputSchema: {}
|
|
7391
7656
|
}, async () => {
|
|
7392
7657
|
const result = await executionStatus(getConfig(), doFetch);
|
|
@@ -7433,7 +7698,7 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
|
|
|
7433
7698
|
|
|
7434
7699
|
` + "Arguments: `mint` and `side` are required. Amounts are DECIMAL, never raw base units " + '(amount: "0.5", not lamports). Omitting the amount on a sell sells the whole ' + `position.
|
|
7435
7700
|
|
|
7436
|
-
` + "Plans: every plan can buy and sell Candle-launched tokens
|
|
7701
|
+
` + "Plans: every plan can buy and sell Candle-launched tokens, base assets, and a token " + "Candle did not launch. Each trade pays the " + `plan's agent fee on top (feeBps in candle_get_plans).
|
|
7437
7702
|
|
|
7438
7703
|
` + "Pass `paper: true` to rehearse: every admission rule runs and the quote is recorded, but " + "nothing broadcasts and no funds move. A paper buy credits this key's paper inventory, " + "including for external Solana mints routed through Jupiter. A later sell by amount or " + "percent closes that book without reading the live wallet and without MARKET_NOT_FOUND " + "-- including when `paper` is omitted on the exit, as long as the paper position exists. " + "Do this before the first live trade of a new strategy, and whenever you are unsure a " + `trade would be admitted at all.
|
|
7439
7704
|
|
|
@@ -7657,8 +7922,9 @@ async function registerEligibleProfileTool(server, env = process.env, doFetch =
|
|
|
7657
7922
|
// src/server.ts
|
|
7658
7923
|
var INSTRUCTIONS = `Candle is a trading and token-launch rail for agents. You hold a scoped API key, never a private key; signing and funding stay with the key owner's wallet.
|
|
7659
7924
|
|
|
7660
|
-
START HERE —
|
|
7925
|
+
START HERE — fifteen tools need NO credential. Call these first to confirm the server is wired before asking anyone for anything:
|
|
7661
7926
|
candle_get_market price, market cap, volume, curve state for one token
|
|
7927
|
+
candle_get_price_history one Solana token's OHLC bars in USD (1m to 1d, up to 500, page back with to); source and volumeUnit say whose bars
|
|
7662
7928
|
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)
|
|
7663
7929
|
candle_token_page_about public About summary, switches default off
|
|
7664
7930
|
candle_token_page_burns completed CNDL burn history
|
|
@@ -7673,7 +7939,7 @@ START HERE — fourteen tools need NO credential. Call these first to confirm th
|
|
|
7673
7939
|
candle_token_events one token's signals, callouts and tracked-wallet trades, newest first, W or L on sells. kind filters the page on this server; page with nextCursor/before until null. Off until the deployment switches it on (503 TOKEN_EVENTS_DISABLED)
|
|
7674
7940
|
candle_token_card one token's card data: market, all-time high since launch, safety and same-name facts, tracked wallets in the last hour, each block with its source; null means no source, not zero. Off until the deployment switches it on (503 TOKEN_CARD_DISABLED)
|
|
7675
7941
|
candle_resolve_token a ticker or partial name -> mint address + chain
|
|
7676
|
-
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
|
|
7942
|
+
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. deployer.priorLaunches is counted (pump.fun curves for a curve creator; record.count says the source); deployer.launcher is the wallet that sent the launch, often a launch tool's signer, never the developer; launch.deployerLaunches is that wallet's inclusive informational count, never a warning
|
|
7677
7943
|
candle_token_clusters optional third-party linked-wallet estimates for a Solana token, always a result: usually status unavailable with a reason, which is not zero clustered supply or clearance. Report its caveat with any figure; keep it separate from forensics
|
|
7678
7944
|
candle_get_agent_profile your own tier, caps and verified activity
|
|
7679
7945
|
candle_list_agents the agent directory: every published agent with its strategy, declared models, all-time P&L, trades and launches; sort newest, active or pnl
|