Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mcp-docs-reconcile.md
Original file line number Diff line number Diff line change
@@ -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`.
57 changes: 56 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: <your-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": "<your-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": "<your-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.
Expand Down Expand Up @@ -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. |
Expand Down
5 changes: 5 additions & 0 deletions src/__tests__/response-meta.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/response-meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Loading