🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@anaxer/mcp

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@anaxer/mcp - npm Package Compare versions

Comparing version
0.0.1
to
0.1.0
+334
dist/index.js
#!/usr/bin/env node
// src/index.ts
import { connect } from "@anaxer/sdk";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// src/config.ts
var ConfigError = class extends Error {
constructor(message) {
super(message);
this.name = "ConfigError";
}
};
function parseConfig(env = process.env) {
const apiKey = env.ANAXER_API_KEY?.trim();
if (!apiKey) {
throw new ConfigError(
"ANAXER_API_KEY is required. Set it in your MCP client env config (e.g. Claude Desktop / Cursor mcpServers.*.env)."
);
}
const baseUrl = env.ANAXER_BASE_URL?.trim() || void 0;
const wsUrl = env.ANAXER_WS_URL?.trim() || void 0;
return { apiKey, baseUrl, wsUrl };
}
// src/server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
// src/tools/markets.ts
import { z } from "zod";
// src/format.ts
import { AnaxerError } from "@anaxer/sdk";
function successResult(payload) {
return {
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
};
}
function errorResult(err) {
if (err instanceof AnaxerError) {
return {
isError: true,
content: [{ type: "text", text: `${err.code}: ${err.message}` }]
};
}
const message = err instanceof Error ? err.message : String(err);
return {
isError: true,
content: [{ type: "text", text: `internal: ${message}` }]
};
}
async function runTool(fn) {
try {
return successResult(await fn());
} catch (err) {
return errorResult(err);
}
}
// src/tools/markets.ts
var listLimitSchema = z.number().int().min(1).max(200).default(20).describe(
"Rows per page; default 20 to conserve context \u2014 raise only if needed"
);
function registerMarketTools(server, client) {
server.registerTool(
"list_creations",
{
description: 'One page of recent token creations (launches). Optional singular `source` (e.g. "pump_fun") \u2014 NOT an array; live multi-source use tail_stream with `sources`. Use excludeMayhem=true to drop mayhem-mode launches. To get more, call again with `cursor` set to the previous `next`; stop when `next` is null.',
inputSchema: {
source: z.string().optional().describe('Single source, e.g. "pump_fun" (NOT an array)'),
limit: listLimitSchema,
from: z.union([z.number(), z.string()]).optional(),
to: z.union([z.number(), z.string()]).optional(),
excludeMayhem: z.boolean().optional(),
cursor: z.string().optional().describe("Pass the previous response's `next` for the next page")
}
},
async ({ source, limit, from, to, excludeMayhem, cursor }) => runTool(async () => {
const page = await client.creations({
source,
limit,
from,
to,
excludeMayhem,
cursor
});
return { data: page.data, next: page.next, window: page.window };
})
);
server.registerTool(
"list_graduations",
{
description: "One page of recent graduations/migrations. Optional singular `source` (NOT an array). Use minLiquiditySol / excludeMayhem to filter junk. To get more, call again with `cursor` set to the previous `next`; stop when `next` is null.",
inputSchema: {
source: z.string().optional().describe('Single source, e.g. "pump_fun" (NOT an array)'),
limit: listLimitSchema,
from: z.union([z.number(), z.string()]).optional(),
to: z.union([z.number(), z.string()]).optional(),
excludeMayhem: z.boolean().optional(),
minLiquiditySol: z.number().optional(),
cursor: z.string().optional().describe("Pass the previous response's `next` for the next page")
}
},
async ({ source, limit, from, to, excludeMayhem, minLiquiditySol, cursor }) => runTool(async () => {
const page = await client.graduations({
source,
limit,
from,
to,
excludeMayhem,
minLiquiditySol,
cursor
});
return { data: page.data, next: page.next, window: page.window };
})
);
server.registerTool(
"get_launchpad_stats",
{
description: "Aggregate launchpad stats (creations/graduations counts) over a rolling window. Use for 'which launchpads are hot'. Optional windowHours.",
inputSchema: {
windowHours: z.number().int().positive().optional()
}
},
async ({ windowHours }) => runTool(() => client.launchpads.stats({ windowHours }))
);
}
// src/tools/tail.ts
import { z as z2 } from "zod";
var CHANNELS = ["trades", "creations", "graduations", "prices"];
function registerTailTool(server, client) {
server.registerTool(
"tail_stream",
{
description: "Collect a bounded batch of live WebSocket events, then return and unsubscribe. Always terminates: ends when maxEvents (default 20, cap 100) or timeoutMs (default 10000, cap 30000) hits first. filters is a per-channel bag using WS shapes \u2014 note `sources` is an ARRAY (unlike REST list tools' singular `source`). Valid keys: trades: sources, mints, wallets, minVolumeUsd, maxVolumeUsd; creations: sources, enriched, excludeMayhem; graduations: sources, excludeMayhem, minLiquiditySol; prices: sources, mints. No transfers channel in v1. Use for short live watches / summaries, not history (history \u2192 list_* tools).",
inputSchema: {
channel: z2.enum(CHANNELS),
filters: z2.record(z2.unknown()).optional().describe(
"Per-channel filter bag (WS filter shape). See the tool description for the valid keys per channel; uses `sources` (array), not `source`."
),
maxEvents: z2.number().int().min(1).max(100).default(20),
timeoutMs: z2.number().int().min(1e3).max(3e4).default(1e4)
}
},
async ({ channel, filters, maxEvents, timeoutMs }) => {
try {
const result = await collectTail(client, {
channel,
filters,
maxEvents,
timeoutMs
});
return successResult(result);
} catch (err) {
return errorResult(err);
}
}
);
}
async function collectTail(client, opts) {
const { channel, filters, maxEvents, timeoutMs } = opts;
const events = [];
const sub = client.stream(channel, filters);
let timer;
let settled = false;
let onClientError;
try {
const endedBy = await new Promise((resolve, reject) => {
const settle = (reason) => {
if (settled) return;
settled = true;
resolve(reason);
};
const fail = (err) => {
if (settled) return;
settled = true;
reject(err);
};
const onData = (data) => {
if (settled) return;
events.push(data);
if (events.length >= maxEvents) settle("maxEvents");
};
const onSubError = (err) => {
fail(err);
};
onClientError = (err) => {
fail(err);
};
sub.on("data", onData);
sub.on("error", onSubError);
client.on("error", onClientError);
timer = setTimeout(() => settle("timeout"), timeoutMs);
});
return {
channel,
endedBy,
count: events.length,
events
};
} finally {
if (timer !== void 0) clearTimeout(timer);
if (onClientError) {
client.off("error", onClientError);
}
sub.close();
}
}
// src/tools/tokens.ts
import { z as z3 } from "zod";
var mintSchema = z3.string().min(32).max(44).describe("Base58 mint address");
var listLimitSchema2 = z3.number().int().min(1).max(200).default(20).describe(
"Rows per page; default 20 to conserve context \u2014 raise only if needed"
);
function registerTokenTools(server, client) {
server.registerTool(
"get_token_metadata",
{
description: "Name, symbol, supply, and socials for one mint. Use for 'what is this token' / metadata lookups. 404 \u2192 mint not in Anaxer yet (never observed or resolve failed).",
inputSchema: { mint: mintSchema }
},
async ({ mint }) => runTool(() => client.tokens.get(mint))
);
server.registerTool(
"get_tokens_metadata",
{
description: "Batch metadata for up to 30 mints. Missing/never-observed mints are omitted from the array (not errored). Prefer over many get_token_metadata calls.",
inputSchema: {
mints: z3.array(mintSchema).min(1).max(30).describe("Up to 30 base58 mints")
}
},
async ({ mints }) => runTool(() => client.tokens.batch(mints))
);
server.registerTool(
"get_token_price",
{
description: "Latest USD + SOL price and market cap for one mint. Use for 'what's X worth now'. 404 \u2192 the mint has no live price.",
inputSchema: { mint: mintSchema }
},
async ({ mint }) => runTool(() => client.tokens.price(mint))
);
server.registerTool(
"get_token_prices",
{
description: "Batch latest prices for up to 30 mints. Missing prices are omitted from the array. Prefer over many get_token_price calls.",
inputSchema: {
mints: z3.array(mintSchema).min(1).max(30).describe("Up to 30 base58 mints")
}
},
async ({ mints }) => runTool(() => client.tokens.batchPrice(mints))
);
server.registerTool(
"list_token_trades",
{
description: 'One page of recent swaps for a mint (REST history, not a live tail). Optional singular `source` filter (e.g. "pump_fun") \u2014 NOT an array; for live multi-source filtering use tail_stream with `sources`. To get more, call again with `cursor` set to the previous `next`; stop when `next` is null. Prefer limit \u2264 20 (default) to conserve context.',
inputSchema: {
mint: mintSchema,
limit: listLimitSchema2,
from: z3.union([z3.number(), z3.string()]).optional().describe("Unix ms or ISO-8601 start bound"),
to: z3.union([z3.number(), z3.string()]).optional().describe("Unix ms or ISO-8601 end bound"),
source: z3.string().optional().describe('Single source, e.g. "pump_fun" (NOT an array)'),
cursor: z3.string().optional().describe("Pass the previous response's `next` for the next page")
}
},
async ({ mint, limit, from, to, source, cursor }) => runTool(async () => {
const page = await client.tokens.trades(mint, {
limit,
from,
to,
source,
cursor
});
return { data: page.data, next: page.next, window: page.window };
})
);
}
// src/server.ts
function buildServer(client) {
const server = new McpServer({
name: "anaxer",
version: "0.1.0"
});
registerTokenTools(server, client);
registerMarketTools(server, client);
registerTailTool(server, client);
return server;
}
// src/index.ts
async function main() {
let config;
try {
config = parseConfig();
} catch (err) {
const message = err instanceof ConfigError ? err.message : err instanceof Error ? err.message : String(err);
console.error(message);
process.exit(1);
}
const client = connect({
apiKey: config.apiKey,
baseUrl: config.baseUrl,
wsUrl: config.wsUrl
});
const server = buildServer(client);
const transport = new StdioServerTransport();
const shutdown = async () => {
try {
await client.close();
} catch (err) {
console.error(
"anaxer-mcp: error during client.close():",
err instanceof Error ? err.message : err
);
}
process.exit(0);
};
process.once("SIGINT", () => {
void shutdown();
});
process.once("SIGTERM", () => {
void shutdown();
});
await server.connect(transport);
}
main().catch((err) => {
console.error(
"anaxer-mcp: fatal:",
err instanceof Error ? err.message : err
);
process.exit(1);
});
+25
-20
{
"name": "@anaxer/mcp",
"version": "0.0.1",
"description": "Official Anaxer MCP server for Claude and Cursor. Not available yet - this is a placeholder release that reserves the package name.",
"version": "0.1.0",
"description": "Official MCP server for Anaxer — query Solana DEX data from Claude Desktop, Claude Code, Cursor, and other MCP hosts.",
"license": "MIT",
"type": "module",
"bin": {
"anaxer-mcp": "bin/cli.js"
"anaxer-mcp": "./dist/index.js"
},
"files": [
"bin",
"README.md",
"LICENSE"
],
"license": "MIT",
"homepage": "https://anaxer.com/docs",
"bugs": "https://anaxer.com/contact",
"keywords": [
"mcp",
"model-context-protocol",
"solana",
"claude",
"cursor",
"pump.fun"
],
"files": ["dist", "README.md"],
"engines": {
"node": ">=18"
"node": ">=20"
},
"scripts": {
"build": "tsup && node ./scripts/assert-no-stdout-logs.mjs",
"assert-stdio": "node ./scripts/assert-no-stdout-logs.mjs",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"prepublishOnly": "npm run build"
},
"dependencies": {
"@anaxer/sdk": "^0.1.0",
"@modelcontextprotocol/sdk": "^1.29.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/node": "^20.16.10",
"tsup": "^8.5.0",
"typescript": "^5.6.3",
"vitest": "^3.2.4"
},
"publishConfig": {

@@ -28,0 +33,0 @@ "access": "public"

+86
-13

@@ -1,24 +0,97 @@

# @anaxer/mcp
# `@anaxer/mcp`
> This package name is reserved for the official Anaxer MCP server. It is **not available yet.**
Official [MCP](https://modelcontextprotocol.io) server for the Anaxer Solana real-time
data API. Add a few lines to Claude Desktop, Claude Code, or Cursor and the assistant
can call typed tools over your API key — prices, metadata, creations, graduations, and a
bounded live stream tail.
[Anaxer](https://anaxer.com) is a real-time Solana data API — WebSocket streams and REST for Pump.fun, PumpSwap, and more. The real MCP server will let Claude, Cursor, and other MCP-compatible agents query live Solana data directly.
**Requires Node ≥ 20.** This package wraps [`@anaxer/sdk`](https://www.npmjs.com/package/@anaxer/sdk);
it adds no gateway or wire behavior.
This is a placeholder release. It exists only to reserve the `@anaxer/mcp` package name and protect users from a malicious server being published under it before the real one ships — this matters more than most placeholders, since MCP configs commonly run `npx -y @anaxer/mcp`, which installs and executes whatever is published under this name with no confirmation prompt.
```bash
npx -y @anaxer/mcp
```
Running it prints a clear message and exits — it does not silently pretend to work or implement any part of the MCP protocol:
## Setup
Set `ANAXER_API_KEY` in the MCP host config (never pass the key as a tool argument).
### Claude Desktop / Claude Code / Cursor
```jsonc
{
"mcpServers": {
"anaxer": {
"command": "npx",
"args": ["-y", "@anaxer/mcp"],
"env": {
"ANAXER_API_KEY": "sk_live_…"
}
}
}
}
```
$ npx -y @anaxer/mcp
@anaxer/mcp is not available yet.
This package name is reserved for the official Anaxer MCP server to
prevent impersonation. Follow progress and get notified at
https://anaxer.com/docs
```
**Follow progress:** https://anaxer.com/docs
**Status:** not yet released.
Optional:
| Env | Default | Purpose |
|---|---|---|
| `ANAXER_BASE_URL` | `https://api.anaxer.com` | REST base (local gateway: `http://localhost:3010`) |
| `ANAXER_WS_URL` | derived from base → `…/v1/stream` | Override WebSocket URL |
## Tools
| Tool | What it answers |
|---|---|
| `get_token_metadata` | Name/symbol/supply/socials for one mint |
| `get_tokens_metadata` | Batch metadata (≤30 mints) |
| `get_token_price` | Latest USD + SOL price / market cap for one mint |
| `get_token_prices` | Batch prices (≤30 mints) |
| `list_token_trades` | One page of recent swaps for a mint (REST history) |
| `list_creations` | One page of recent launches |
| `list_graduations` | One page of recent graduations |
| `get_launchpad_stats` | Aggregate launchpad stats over a window |
| `tail_stream` | Bounded live WS batch (`trades` / `creations` / `graduations` / `prices`) |
List tools default to `limit: 20` (max 200) to conserve model context. Paginate by calling
again with `cursor` set to the previous `next`; stop when `next` is null.
**Spelling:** REST list tools take a singular `source` string. `tail_stream` filters use a
`sources` **array** (WebSocket filter shape).
### `tail_stream` caps
- `maxEvents` — default `20`, cap `100`
- `timeoutMs` — default `10_000`, cap `30_000`
- Ends when **either** cap hits first; always unsubscribes before returning
Per-channel filter keys:
- `trades`: `sources`, `mints`, `wallets`, `minVolumeUsd`, `maxVolumeUsd`
- `creations`: `sources`, `enriched`, `excludeMayhem`
- `graduations`: `sources`, `excludeMayhem`, `minLiquiditySol`
- `prices`: `sources`, `mints`
## v1 omissions
These are intentional, not bugs:
- No `tail_stream("transfers")` and no `list_programs` (same as `@anaxer/sdk` v1)
- No remote / hosted MCP (Streamable HTTP) — local **stdio** only
- No gap recovery on reconnect (SDK has none)
- No MCP resources or prompts — tools only
## Troubleshooting
1. **Wrong API key on `tail_stream`.** REST tools fail per call and recover as soon as you
fix `ANAXER_API_KEY`. WebSocket `unauthorized` is **terminal** for the shared socket —
**restart the MCP server process** (reload the host / re-run `npx @anaxer/mcp`) after
fixing the key.
2. **`subscription_limit`.** Each open `tail_stream` holds one plan subscription until it
returns. Overlapping tails (or a leaked subscription) on free/low plans can hit the
cap. The server always `close()`s in `finally`; hosts normally serialize tool calls, so
this is rare.
## License
MIT
#!/usr/bin/env node
"use strict";
console.error(
"\n@anaxer/mcp is not available yet.\n" +
"This package name is reserved for the official Anaxer MCP server to\n" +
"prevent impersonation. Follow progress and get notified at\n" +
"https://anaxer.com/docs\n"
);
process.exit(1);
MIT License
Copyright (c) 2026 Anaxer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.