nansen-cli 1.41.0 → 1.42.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.42.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#508](https://github.com/nansen-ai/nansen-cli/pull/508) [`3fc6e6b`](https://github.com/nansen-ai/nansen-cli/commit/3fc6e6b3083d3a8e0ab738c800bbcaf24dc00bef) Thanks [@gulshngill](https://github.com/gulshngill)! - Add `nansen mcp verify` to verify the hosted Nansen MCP setup with an authenticated data-path check.
8
+
9
+ - [#487](https://github.com/nansen-ai/nansen-cli/pull/487) [`bb3f33e`](https://github.com/nansen-ai/nansen-cli/commit/bb3f33ea28642effbda911012dec8810adc8d40c) Thanks [@gulshngill](https://github.com/gulshngill)! - Add `nansen mcp install <client>` / `nansen mcp uninstall <client>` for one-step setup of the hosted Nansen MCP server (https://mcp.nansen.ai/ra/mcp) in Claude Code, Claude Desktop, and Cursor. Writes are merge-only and atomic (existing servers preserved, `.bak` backup on install and uninstall, refuses unparseable configs), use the API key from `nansen login` / `NANSEN_API_KEY`, never print the key, and support `--dry-run`.
10
+
11
+ ### Patch Changes
12
+
13
+ - [#536](https://github.com/nansen-ai/nansen-cli/pull/536) [`f5e48df`](https://github.com/nansen-ai/nansen-cli/commit/f5e48dfddc9f063225847f54ed4b20334cadc6ae) Thanks [@kome12](https://github.com/kome12)! - `bridge quote` now prints a notice when a Hyperliquid USDC amount is floored to the 6-decimal precision the bridge signs, instead of adjusting the amount silently. The adjustment is unchanged (it's what keeps the persisted amount matching what gets signed); it's just no longer hidden.
14
+
15
+ - [#533](https://github.com/nansen-ai/nansen-cli/pull/533) [`a96418d`](https://github.com/nansen-ai/nansen-cli/commit/a96418d4c7af5d8f8874f7ccb27d1d59ce797098) Thanks [@kome12](https://github.com/kome12)! - Bridge withdrawals now verify the Hyperliquid action's type, amount, network, and source token/routing fields against your request before signing, so a tampered quote cannot inflate a withdrawal, swap in a different token, or authorize on another account. The deposit action's EIP-712 primary type and exact ordered field list are now pinned too — not just the shared signing domain — so a quote can no longer pass every value check yet have the wallet sign a differently shaped Hyperliquid action (e.g. an agent approval) that the amount cap doesn't bound. The relayer authorization step is likewise pinned exactly to its real EIP-712 domain, field shape, and signing wallet, and its signature can only ever be submitted to the relayer's own fixed authorize endpoint — closing a gap where a malicious quote could have requested a signature over unrelated typed data and relayed it elsewhere. All of a withdrawal's steps are verified against this before any of them are signed or posted, so a bad step later in a multi-step quote (e.g. the real [authorize, sendAsset] order) can no longer let an earlier, valid-looking step reach the relayer or Hyperliquid first.
16
+
17
+ Also fixes a false rejection: Hyperliquid withdrawals whose `--amount` was given in base units (the default, no `--amount-unit`) and whose last two digits weren't zero were rejected at execute time as an amount mismatch, because the amount wasn't floored to the 6-decimal precision the bridge actually sends. Base-unit amounts are now floored the same way `--amount-unit` amounts already were, so these withdrawals execute.
18
+
19
+ - [#530](https://github.com/nansen-ai/nansen-cli/pull/530) [`4312b50`](https://github.com/nansen-ai/nansen-cli/commit/4312b5004e0644f0bfcdac2e5e3eda7128958d92) Thanks [@kome12](https://github.com/kome12)! - Trade safety: tolerate a bounded native-token fee on cross-chain bridge swaps. The pre-signing swap-outcome check rejected any non-input token leaving the wallet, which could reject a legitimate bridge that pays its network fee in the native token on a token-input route. The tolerance is capped and applies only to the native token on bridges; every other token, and all same-chain swaps, stay strict.
20
+
21
+ - [#537](https://github.com/nansen-ai/nansen-cli/pull/537) [`2d630d5`](https://github.com/nansen-ai/nansen-cli/commit/2d630d545161d656b41867d7b439e02c0786a01f) Thanks [@kome12](https://github.com/kome12)! - Fix `nansen mcp verify` routing after merging MCP install commands.
22
+
23
+ - [#538](https://github.com/nansen-ai/nansen-cli/pull/538) [`853c48a`](https://github.com/nansen-ai/nansen-cli/commit/853c48ac1779e42507513c7c47a8f57bbffd540d) Thanks [@kome12](https://github.com/kome12)! - Harden the Hyperliquid bridge deposit leg: EVM approvals are now re-scoped to the requested amount (never unlimited) and the deposit target contract/method is pinned, so a tampered quote can't drain the wallet.
24
+
25
+ - [#534](https://github.com/nansen-ai/nansen-cli/pull/534) [`e23af5c`](https://github.com/nansen-ai/nansen-cli/commit/e23af5cd111eb596dda9f3b6c88b32ffa3e5e756) Thanks [@gulshngill](https://github.com/gulshngill)! - Credential hygiene: `nansen login` verification failures cannot relay the API
26
+ key, invalid-key remediation points at key management, and login guidance leads
27
+ with paths that avoid shell history. Every request that carries a credential —
28
+ API key, agent, limit-order JWT/X-API-Key, MCP verify, and Privy auth — now
29
+ refuses to follow HTTP redirects, so a credential can't be relayed to a redirect
30
+ target. Interactive password and API-key prompts stay masked (no cleartext echo)
31
+ even when stdout is redirected.
32
+
33
+ - [#529](https://github.com/nansen-ai/nansen-cli/pull/529) [`45b8584`](https://github.com/nansen-ai/nansen-cli/commit/45b8584ba8e2a3859612d8702dd78f117911cf87) Thanks [@gulshngill](https://github.com/gulshngill)! - Docs: stop pointing at the retired Cursor install deep link, pin the
34
+ `mcp-remote` bridge to the version `mcp install` writes, and correct the
35
+ header-formatting note (whitespace after the colon is trimmed; the key belongs
36
+ in `env`, not in the argument list).
37
+
38
+ - [#531](https://github.com/nansen-ai/nansen-cli/pull/531) [`9bb44a7`](https://github.com/nansen-ai/nansen-cli/commit/9bb44a7339e80bba30d9ec2e499b5d3e306ceb66) Thanks [@crazywriter1](https://github.com/crazywriter1)! - Honor the original HTTP method on x402 paid retries so GET/DELETE/PATCH requests are not resent as POST after payment.
39
+
40
+ ## 1.41.1
41
+
42
+ ### Patch Changes
43
+
44
+ - [#527](https://github.com/nansen-ai/nansen-cli/pull/527) [`02efb6d`](https://github.com/nansen-ai/nansen-cli/commit/02efb6d1f40453c03135eb68cf493486a5b6133a) Thanks [@kome12](https://github.com/kome12)! - Cross-chain (bridge) swaps now run swap-outcome verification instead of skipping it entirely. The output-arrival check is still skipped (the output settles on the destination chain), but the input-outflow cap and no-sibling-drain checks now run on the source-chain leg, closing a gap where a compromised quote's bridge instructions could move more than the declared input. Bridges also now enforce an intent-relative lower bound on the source-chain input outflow (an exactIn bridge must spend ~the requested input, so a large fee-only or partial no-op no longer verifies) and still validate the quote's output-amount integrity, and the native-SOL bridge log no longer contradicts itself about whether the output check ran. Note the lower bound relaxes by a native-SOL fee/rent allowance (~0.013 SOL), so on a small native-SOL leg at or below that allowance the floor effectively collapses to a bare "outflow > 0" — the tightest bound possible for a native leg whose fees are indistinguishable from the transfer. `--swap-mode` is now validated against `exactIn`/`exactOut` at the CLI, and both the swap-outcome verifier and the pre-signing request-intent completeness checks fail closed on an unrecognized mode in a persisted quote so a garbage value cannot bypass the exactIn input floor — even when outcome verification is skipped or degraded.
45
+
46
+ Because bridges now go through the simulation, a bridge quote that **reverts in simulation** returns `proceed: false` and is dropped (the signing loop falls through to the next quote); only a simulation that cannot run at all (`NO_SIM_RPC` / `SIM_RPC_ERROR` / `NOT_SIM_CAPABLE`) degrades to proceed-without-verification, matching same-chain swaps. This is a new, fail-closed outcome for bridges specifically.
47
+
48
+ - [#513](https://github.com/nansen-ai/nansen-cli/pull/513) [`55eb953`](https://github.com/nansen-ai/nansen-cli/commit/55eb953cc15fe21aa441d1700e05ef053c643a58) Thanks [@kome12](https://github.com/kome12)! - Fix `trade execute` crashing on Solana-source bridge quotes from the Relay aggregator, which return raw uncompiled instructions instead of a ready-to-sign transaction. These are now compiled client-side before signing.
49
+
3
50
  ## 1.41.0
4
51
 
5
52
  ### Minor Changes
package/README.md CHANGED
@@ -20,17 +20,27 @@ Three options — pick whichever fits your setup:
20
20
 
21
21
  1. **API key** (subscription):
22
22
  ```bash
23
- nansen login --api-key <key> # save key to ~/.nansen/config.json
24
- nansen login --human # interactive prompt
25
- export NANSEN_API_KEY=... # env var (highest priority)
26
- nansen logout # remove saved key
23
+ nansen login --human # interactive prompt; saves to ~/.nansen/config.json
24
+ nansen login # uses NANSEN_API_KEY when already set
25
+ nansen logout # remove saved key
27
26
  ```
27
+ For automation, inject `NANSEN_API_KEY` through your environment or secret manager.
28
28
  Get your API key at [app.nansen.ai/auth/agent-setup](https://app.nansen.ai/auth/agent-setup).
29
29
 
30
30
  2. **x402 micropayment** (no key needed): `nansen wallet create`, fund with USDC on Base or Solana, or USDT0 on X Layer, then call any endpoint — the CLI signs `Payment-Signature` headers automatically on 402 responses. See [Wallet](#wallet).
31
31
 
32
32
  3. **MPP via tempo** (no key needed): install the [tempo CLI](https://docs.tempo.xyz) separately, run `tempo wallet login` to set up, then call the Nansen API through `tempo request`. The Nansen API selects the MPP rail when it sees `Authorization: Payment ...`. See [MPP / Tempo](#mpp--tempo) below.
33
33
 
34
+ ## Verify your MCP setup
35
+
36
+ For the hosted Nansen MCP server, verify server reachability and the supplied API key on the paid data path with:
37
+
38
+ ```bash
39
+ npx -y nansen-cli mcp verify # uses the saved key or NANSEN_API_KEY
40
+ ```
41
+
42
+ The check calls `tools/list` for reachability, then calls the paid `nansen_score_top_tokens` canary tool. A successful canary costs about 1 credit; tool listings and free tools alone do not prove that a key works. The CLI cannot inspect the key inside your MCP client, so make sure this same key is in the client's `NANSEN-API-KEY` header. For the final client-config check, ask your client: “Use the `nansen_score_top_tokens` tool.”
43
+
34
44
  ## Commands
35
45
 
36
46
  ```
@@ -40,6 +50,7 @@ nansen agent "<question>" --expert # deeper analysis (750 credits, Pro)
40
50
  nansen trade quote --chain solana --from SOL --to USDC --amount 1000000000
41
51
  nansen trade execute --quote <quoteId>
42
52
  nansen wallet <subcommand> [options]
53
+ nansen mcp install <client> # add the Nansen MCP server to Claude Code/Desktop or Cursor
43
54
  nansen schema [command] [--pretty] # full command reference (no API key needed)
44
55
  ```
45
56
 
@@ -59,7 +70,19 @@ Connect any MCP client to Nansen's streamable HTTP server:
59
70
  - **Authentication:** `NANSEN-API-KEY` header
60
71
  - **API key:** [app.nansen.ai/auth/agent-setup](https://app.nansen.ai/auth/agent-setup)
61
72
 
62
- **Claude Desktop and Cursor:** setup instructions for both — the Claude Desktop `.dxt` bundle and the Cursor install deep link — are in the connection docs: [docs.nansen.ai/mcp/connecting](https://docs.nansen.ai/mcp/connecting).
73
+ One-step install of the hosted [Nansen MCP server](https://docs.nansen.ai/mcp/overview) (`https://mcp.nansen.ai/ra/mcp`) into a local MCP client:
74
+
75
+ ```bash
76
+ nansen mcp install claude-code # ~/.claude.json (user scope)
77
+ nansen mcp install claude-desktop # macOS/Windows only; bridges via pinned mcp-remote
78
+ nansen mcp install cursor # ~/.cursor/mcp.json
79
+ nansen mcp install cursor --dry-run # print what would be written (key redacted)
80
+ nansen mcp uninstall <client> # remove the entry (add --dry-run to preview)
81
+ ```
82
+
83
+ Uses the API key from `nansen login` / `NANSEN_API_KEY`; re-run `install` after rotating your key. Writes are merge-only and atomic: existing servers and settings are preserved, a `.bak` copy is written before every install or uninstall, and the CLI refuses to touch a config it can't parse. Note the client config stores the API key in plaintext — new files are created with `0600` permissions. Restart the client after installing.
84
+
85
+ **Manual setup**, for other clients or if you would rather not use the CLI — the paths below, and the connection docs at [docs.nansen.ai/mcp/connecting](https://docs.nansen.ai/mcp/connecting).
63
86
 
64
87
  **One-command (Claude Code):**
65
88
 
@@ -82,7 +105,7 @@ claude mcp add --transport http nansen https://mcp.nansen.ai/ra/mcp --header "NA
82
105
  }
83
106
  ```
84
107
 
85
- **Manual (stdio-only clients):** use `mcp-remote` as a bridge. Keep the header as one argument with no space after the colon:
108
+ **Manual (stdio-only clients):** use `mcp-remote` as a bridge. Keep the header name and value in a single `args` entry, and keep the key in `env` rather than in the argument list `mcp-remote` substitutes `${NANSEN_API_KEY}` from the environment:
86
109
 
87
110
  ```json
88
111
  {
@@ -91,7 +114,7 @@ claude mcp add --transport http nansen https://mcp.nansen.ai/ra/mcp --header "NA
91
114
  "command": "npx",
92
115
  "args": [
93
116
  "-y",
94
- "mcp-remote@latest",
117
+ "mcp-remote@0.2.1",
95
118
  "https://mcp.nansen.ai/ra/mcp",
96
119
  "--header",
97
120
  "NANSEN-API-KEY:${NANSEN_API_KEY}"
@@ -104,6 +127,8 @@ claude mcp add --transport http nansen https://mcp.nansen.ai/ra/mcp --header "NA
104
127
  }
105
128
  ```
106
129
 
130
+ `mcp-remote` is pinned to an exact version rather than `@latest` because the bridge handles your API key on every request, and `npx` would otherwise pull a new release automatically. `0.2.1` is the current release and the version this config is tested against; bumping it is safe — review the release and update the pin.
131
+
107
132
  **Claude Tag (Claude in Slack):** an admin must attach a plugin whose `.mcp.json` points at `https://mcp.nansen.ai/ra/mcp` and add a custom credential allowing the host `mcp.nansen.ai`. See the [Claude Tag custom-connections documentation](https://claude.com/docs/claude-tag/admins/connections/custom). Per-user fallback: use Claude Code or Claude Desktop.
108
133
 
109
134
  ## Trading
@@ -294,6 +319,7 @@ Any field may be absent or `null`, meaning unknown — never assume zero. A low-
294
319
  | `command not found` | `npm install -g nansen-cli` |
295
320
  | Global install reports an older version | `npm i -g nansen-cli@latest --registry=https://registry.npmjs.org/ --prefer-online`, then check `which -a nansen` for stale binaries |
296
321
  | `UNAUTHORIZED` after login | `nansen auth status` shows which key is active and where it comes from; re-run `nansen login` or set `NANSEN_API_KEY` |
322
+ | MCP client lists tools but paid calls fail | Run `npx -y nansen-cli mcp verify` with the saved key or `NANSEN_API_KEY`, and ensure that same key is in the client's `NANSEN-API-KEY` header |
297
323
  | Anything else misbehaving | `nansen doctor` checks your whole setup (auth, wallets, caches, connectivity) with a fix per finding |
298
324
  | Empty perp _research_ results | Use `--symbol BTC`, not `--token`. Perps are Hyperliquid-only. |
299
325
  | `perp` _trading_ prints the usage banner | Trading needs `--coin BTC` (`--symbol` also works); see the Perpetuals section. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nansen-cli",
3
- "version": "1.41.0",
3
+ "version": "1.42.0",
4
4
  "description": "AI-agent CLI for Nansen API analytics, DEX swaps, and cross-chain trading",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -132,7 +132,7 @@ async function installSkill() {
132
132
  async function testQuery() {
133
133
  if (!isLoggedIn()) {
134
134
  log();
135
- log(`Not logged in yet. Run ${CYAN}nansen login --api-key <key>${RESET} to authenticate.`);
135
+ log(`Not logged in yet. Run ${CYAN}nansen login --human${RESET} to authenticate.`);
136
136
  log(`Get your API key at: ${CYAN}https://app.nansen.ai/auth/agent-setup${RESET}`);
137
137
  return;
138
138
  }
@@ -156,7 +156,7 @@ async function testQuery() {
156
156
  log(`${GREEN}✓${RESET} All set! Run ${CYAN}nansen help${RESET} to see all available commands.`);
157
157
  } else {
158
158
  log();
159
- log(`${YELLOW}Query failed. Check your API key with: nansen login --api-key <key>${RESET}`);
159
+ log(`${YELLOW}Query failed. Check your API key with: nansen auth status${RESET}`);
160
160
  }
161
161
  }
162
162
 
@@ -172,7 +172,7 @@ async function main() {
172
172
  log(`${BOLD}Nansen CLI installed!${RESET}`);
173
173
  log();
174
174
  log(`Tip: Run '${CYAN}npx skills add ${SKILL_REPO}${RESET}' to install the Nansen AI coding skill.`);
175
- log(`Tip: Run '${CYAN}nansen login --api-key <key>${RESET}' to authenticate.`);
175
+ log(`Tip: Run '${CYAN}nansen login --human${RESET}' to authenticate.`);
176
176
  log(`Tip: To trade, first create a wallet with '${CYAN}nansen wallet create${RESET}', then quote with '${CYAN}nansen trade quote --chain solana --from SOL --to USDC --amount 1000000000${RESET}' and execute with '${CYAN}nansen trade execute --quote <id>${RESET}'.`);
177
177
  return;
178
178
  }
@@ -21,10 +21,10 @@ allowed-tools: Bash(nansen:*)
21
21
  ## Auth Setup
22
22
 
23
23
  ```bash
24
- # Save API key (non-interactive)
25
- nansen login --api-key <key>
26
- # Or via env var:
27
- NANSEN_API_KEY=<key> nansen login
24
+ # Save API key interactively
25
+ nansen login --human
26
+ # Or use NANSEN_API_KEY after provisioning it through your environment/secret manager
27
+ nansen login
28
28
 
29
29
  # Verify
30
30
  nansen research profiler labels --address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --chain ethereum
@@ -190,7 +190,7 @@ For detailed migration steps (from `~/.nansen/.env`, `.credentials`, or env-var-
190
190
  | Var | Purpose |
191
191
  |-----|---------|
192
192
  | `NANSEN_WALLET_PASSWORD` | Wallet encryption password — only needed for initial `wallet create`. After that, the OS keychain handles it. |
193
- | `NANSEN_API_KEY` | API key (also set via `nansen login --api-key <key>`) |
193
+ | `NANSEN_API_KEY` | API key (can also be saved via `nansen login --human`) |
194
194
  | `PRIVY_APP_ID` | Privy application ID (required for `--provider privy`) |
195
195
  | `PRIVY_APP_SECRET` | Privy application secret (required for `--provider privy`) |
196
196
  | `NANSEN_WALLET_PROVIDER` | Default provider for wallet create — `local` or `privy` |
package/src/api.js CHANGED
@@ -538,7 +538,7 @@ export class NansenAPI {
538
538
  * @param {string|null} network - x402 network string for balance check, e.g. "eip155:8453"
539
539
  * @param {string} url - Request URL
540
540
  * @param {object} body - Request body (will be cleaned)
541
- * @param {object} [options={}] - Request options (may include .headers)
541
+ * @param {object} [options={}] - Request options (may include .method, .headers)
542
542
  * @returns {Promise<object|null>} Parsed JSON on success, null if rejected
543
543
  *
544
544
  * TODO: full fix — extract the entire x402 provider dispatch from request() into
@@ -546,10 +546,15 @@ export class NansenAPI {
546
546
  * touching that one method, not hunting inside the retry loop.
547
547
  */
548
548
  async _x402Retry(signature, walletLabel, network, url, body, options = {}, asset = null) {
549
+ // Mirror request(): paid retries must use the original method. Hardcoding
550
+ // POST burned a payment signature then hit the wrong route for GET/DELETE/PATCH.
551
+ const method = options.method || 'POST';
552
+ const isGet = method === 'GET';
549
553
  const paidResponse = await fetch(url, {
550
- method: 'POST',
554
+ method,
555
+ redirect: 'error',
551
556
  headers: {
552
- 'Content-Type': 'application/json',
557
+ ...(!isGet && { 'Content-Type': 'application/json' }),
553
558
  'X-Client-Type': 'nansen-cli',
554
559
  'X-Client-Version': packageVersion,
555
560
  ...telemetryHeaders(),
@@ -557,7 +562,7 @@ export class NansenAPI {
557
562
  ...this.defaultHeaders,
558
563
  ...options.headers,
559
564
  },
560
- body: JSON.stringify(NansenAPI.cleanBody(body)),
565
+ ...(!isGet && method !== 'DELETE' && { body: JSON.stringify(NansenAPI.cleanBody(body)) }),
561
566
  });
562
567
  if (!paidResponse.ok) return null;
563
568
  if (walletLabel) {
@@ -605,6 +610,7 @@ export class NansenAPI {
605
610
  const isGet = method === 'GET';
606
611
  response = await fetch(url, {
607
612
  method,
613
+ redirect: 'error',
608
614
  headers: {
609
615
  ...(!isGet && { 'Content-Type': 'application/json' }),
610
616
  'X-Client-Type': 'nansen-cli',
@@ -747,7 +753,7 @@ export class NansenAPI {
747
753
  } catch (x402Err) {
748
754
  if (!this.apiKey) {
749
755
  message = 'No API key configured. Three ways to authenticate:\n' +
750
- ' 1. API key: nansen login --api-key <key> (get key at https://app.nansen.ai/auth/agent-setup)\n' +
756
+ ' 1. API key: run `nansen login --human` or set NANSEN_API_KEY (get key at https://app.nansen.ai/auth/agent-setup)\n' +
751
757
  ' 2. x402 micropayment: nansen wallet create + fund with USDC on Base/Solana or USDT0 on X Layer (no API key needed)\n' +
752
758
  ' 3. MPP via tempo: install tempo CLI, run `tempo wallet login`, then call the API with `tempo request` (see skills/nansen-mpp-payment)';
753
759
  } else {