diff --git a/.changeset/mcp-docs-reconcile.md b/.changeset/mcp-docs-reconcile.md new file mode 100644 index 00000000..5d0be509 --- /dev/null +++ b/.changeset/mcp-docs-reconcile.md @@ -0,0 +1,5 @@ +--- +"nansen-cli": patch +--- + +Add a canonical MCP setup section to the README — endpoint `https://mcp.nansen.ai/ra/mcp`, `NANSEN-API-KEY` auth, per-client setup paths for Claude Code, Claude Tag, and generic or stdio-only clients, plus a pointer to the connection docs for Claude Desktop and Cursor — and point the out-of-credits and low-credit warnings at the credits tab of the billing page, `app.nansen.ai/api?tab=api`, instead of the bare `app.nansen.ai/api`. diff --git a/README.md b/README.md index d0fbdd6d..46c24820 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,61 @@ nansen schema [command] [--pretty] # full command reference (no API key neede Run `nansen schema --pretty` for the full subcommand and field reference. +## MCP + +Connect any MCP client to Nansen's streamable HTTP server: + +- **Endpoint:** `https://mcp.nansen.ai/ra/mcp` +- **Authentication:** `NANSEN-API-KEY` header +- **API key:** [app.nansen.ai/auth/agent-setup](https://app.nansen.ai/auth/agent-setup) + +**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). + +**One-command (Claude Code):** + +```bash +claude mcp add --transport http nansen https://mcp.nansen.ai/ra/mcp --header "NANSEN-API-KEY: " +``` + +**Manual (any streamable-HTTP client):** for example, add this to Cursor's `~/.cursor/mcp.json`: + +```json +{ + "mcpServers": { + "nansen": { + "url": "https://mcp.nansen.ai/ra/mcp", + "headers": { + "NANSEN-API-KEY": "" + } + } + } +} +``` + +**Manual (stdio-only clients):** use `mcp-remote` as a bridge. Keep the header as one argument with no space after the colon: + +```json +{ + "mcpServers": { + "nansen": { + "command": "npx", + "args": [ + "-y", + "mcp-remote@latest", + "https://mcp.nansen.ai/ra/mcp", + "--header", + "NANSEN-API-KEY:${NANSEN_API_KEY}" + ], + "env": { + "NANSEN_API_KEY": "" + } + } + } +} +``` + +**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. + ## Trading DEX swaps on `solana` and `base`. Two-step: quote then execute. @@ -216,7 +271,7 @@ nansen research smart-money netflow --chain solana --fields token_symbol,net_flo | Code | Action | |------|--------| -| `CREDITS_EXHAUSTED` | Stop all API calls immediately. `details.credits.remaining` is your actual balance. Top up at [app.nansen.ai/api](https://app.nansen.ai/api). | +| `CREDITS_EXHAUSTED` | Stop all API calls immediately. `details.credits.remaining` is your actual balance. Top up at [app.nansen.ai/api?tab=api](https://app.nansen.ai/api?tab=api). | | `UNAUTHORIZED` | Wrong or missing key. Re-auth. | | `RATE_LIMITED` | Auto-retried by CLI. `details.rateLimit.resetSeconds` is how long the window needs to drain. | | `UNSUPPORTED_FILTER` | Remove the filter and retry. | diff --git a/src/__tests__/response-meta.test.js b/src/__tests__/response-meta.test.js index 55ca72a4..9d58ec36 100644 --- a/src/__tests__/response-meta.test.js +++ b/src/__tests__/response-meta.test.js @@ -169,12 +169,17 @@ describe('creditWarning', () => { it('warns when the balance is exhausted', () => { const warning = creditWarning({ credits: { used: 5, remaining: 0 } }); expect(warning).toContain('Out of API credits'); + expect(warning).toContain('https://app.nansen.ai/api?tab=api'); + // Top-ups live on the billing page; auth/agent-setup is for MCP setup and API keys. + expect(warning).not.toContain('auth/agent-setup'); }); it('warns when the balance will not cover another call of the same size', () => { const warning = creditWarning({ credits: { used: 10, remaining: 3 } }); expect(warning).toContain('3 API credits left'); expect(warning).toContain('less than this call cost (10)'); + expect(warning).toContain('https://app.nansen.ai/api?tab=api'); + expect(warning).not.toContain('auth/agent-setup'); }); it('singularises one remaining credit', () => { diff --git a/src/response-meta.js b/src/response-meta.js index 55adea5f..972a8ad8 100644 --- a/src/response-meta.js +++ b/src/response-meta.js @@ -118,12 +118,12 @@ export function creditWarning(meta) { const { used, remaining, cost } = credits; if (remaining === null) return null; if (remaining === 0) { - return '⚠️ Out of API credits. Top up at https://app.nansen.ai/api'; + return '⚠️ Out of API credits. Top up at https://app.nansen.ai/api?tab=api'; } // The cost header is the authoritative charge; used is the fallback. const charged = cost ?? used; if (charged !== null && charged > 0 && remaining < charged) { - return `⚠️ ${remaining} API credit${remaining === 1 ? '' : 's'} left — less than this call cost (${charged}). Top up at https://app.nansen.ai/api`; + return `⚠️ ${remaining} API credit${remaining === 1 ? '' : 's'} left — less than this call cost (${charged}). Top up at https://app.nansen.ai/api?tab=api`; } return null; }