@zypheragent/mcp 0.4.0 → 0.5.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/README.md CHANGED
@@ -63,7 +63,7 @@ Generic stdio client, with optional environment:
63
63
  }
64
64
  ```
65
65
 
66
- With a valid token the server registers nine more tools (24 in total) and its ready line says `read + EXECUTION`. A malformed token prints a clear error on stderr and execution stays disabled; the 15 read tools keep working.
66
+ With a valid token the server registers eleven more tools (28 in total) and its ready line says `read + EXECUTION`. A malformed token prints a clear error on stderr and execution stays disabled; the 17 read tools keep working.
67
67
 
68
68
  | Tool | What it does | Arguments |
69
69
  | --- | --- | --- |
@@ -72,16 +72,18 @@ With a valid token the server registers nine more tools (24 in total) and its re
72
72
  | `agent_elysium_swap` | Swap on Elysium 99901 (native HYPE <-> a Boxa-indexed token) from the Agent Wallet. | `tokenIn`, `tokenOut` (HYPE on one side; symbol or 0x address), `amountIn`, optional `slippageBps` |
73
73
  | `agent_elysium_launch` | **Launch a token on Elysium** through Boxa's V3 launcher from the Agent Wallet (1B supply in locked liquidity, no creation fee). | `name`, `symbol`, optional `description`, `image`/`website`/`twitter`/`telegram` (https URLs), `allocation` (`balanced`, `creator`, `holders`, `buyback`, `liquidity` or four basis-point shares summing to 10000), `antiSnipe`, `firstBuyHype` |
74
74
  | `agent_bridge_elysium` | Move native HYPE HyperEVM <-> Elysium for the Agent Wallet (fund it before an Elysium swap or launch). | `direction` (`deposit`\|`withdraw`), `amount` |
75
+ | `agent_bridge` | Bridge with Relay between chains (name or id, tokens by symbol/address) from the Agent Wallet. Recipient: the wallet itself or an address on your send allow-list. | `originChain`, `destinationChain`, `amount`, optional `originToken`, `destinationToken`, `recipient` |
76
+ | `upload_token_image` | Upload a token image (PNG/JPEG/GIF/WebP, up to 4 MB, base64 or data URL) and get an https URL for `agent_elysium_launch`. No funds, API token not sent. | `imageBase64` |
75
77
  | `agent_perp_order` | Open/add a Hyperliquid perp position. | `coin` (`BTC`, `xyz:NVDA`), `side` long\|short, `notionalUsd`, `leverage`, optional `tpPrice`, `slPrice`, `reduceOnly` |
76
78
  | `agent_close` | Close a perp position (all or a percentage). | `coin`, optional `fractionPct` (0-100) |
77
79
  | `agent_send` | Send funds from the Agent Wallet to an external 0x address. | `asset`, `amount`, `to` (0x address) |
78
80
  | `agent_transfer_out` | Move funds from the Agent Wallet back to your own main wallet. | `asset`, `amount` |
79
81
 
80
- **These tools move real funds on mainnet.** Every call executes immediately from your Agent Wallet, inside the caps set by you / the operator in the app. There is **no confirmation step** in the MCP, so the assistant is told to ask you first; do not auto-approve these tools in your client. No tool accepts a wallet, owner, URL or path argument: the package can only call the nine fixed paths `/api/agent-api/{status,swap,elysium-swap,elysium-launch,bridge-boxa,perp-order,close,send,transfer-out}` under `ZYPHER_API_URL`.
82
+ **These tools move real funds on mainnet.** Every call executes immediately from your Agent Wallet, inside the caps set by you / the operator in the app. There is **no confirmation step** in the MCP, so the assistant is told to ask you first; do not auto-approve these tools in your client. No tool accepts a wallet, owner, URL or path argument: the package can only call the ten fixed paths `/api/agent-api/{status,swap,elysium-swap,elysium-launch,bridge-boxa,bridge,perp-order,close,send,transfer-out}` (plus the public image upload route of the same host for `upload_token_image`, which never receives the token) under `ZYPHER_API_URL`.
81
83
 
82
84
  Each write call carries a fresh random `Idempotency-Key`. Timeouts are 15 s (45 s for swaps); redirects are refused and responses over 100 KB are refused. After a timeout or a 5xx on a write, the outcome is unknown: call `agent_status` before retrying.
83
85
 
84
- ## Tools (15 read tools, always on)
86
+ ## Tools (17 read tools, always on)
85
87
 
86
88
  | Tool | What it does |
87
89
  | --- | --- |
@@ -90,6 +92,8 @@ Each write call carries a fresh random `Idempotency-Key`. Timeouts are 15 s (45
90
92
  | `get_strong_volume` | Perp tickers with the highest 24h volume, with volume/OI ratio and price change. |
91
93
  | `get_new_pairs` | Newest token launches on Elysium 99901 (all launchpads), newest first. Elysium only; `chain=hyperevm` returns an explicit error. |
92
94
  | `get_elysium_market` | The Elysium market / launchpad feed (like the app's Market and Launch tabs): sort `newest`, `fdv` (top), `volume` (trending), `change` (gainers), `trades`, `holders`; filter by launchpad (`boxa`, `kleos`, `ascend`, `montra`, `volta`, `elyspump`, `uniswap`, `stockium`, `stocks`...), search `q`, `tradable`. Read from the Zypher public API. |
95
+ | `get_elysium_activity` | Recent swaps (or transfers) of an Elysium token, newest first, from chain logs (side, amounts, wallet, block); page backwards with `before`. Times are approximate. |
96
+ | `get_elysium_holders` | Largest holders of an Elysium token seen in recent activity (not a full ranking) plus the index holder count. |
93
97
  | `get_elysium_token` | One Elysium token by address: launchpad, pool, price, FDV, 24h change/volume, holders, tradable flag. |
94
98
  | `analyze_token_health` | Bytecode/ABI check of a token contract (mint, pause, blacklist, owner) with a score and reasons. A heuristic, not an audit. |
95
99
  | `get_social_info` | Recent tweets about a ticker. Needs your own `X_BEARER_TOKEN`; without it the tool returns a clear error. |
@@ -110,7 +114,7 @@ All tool output is JSON text. Text that comes from third parties (token names, t
110
114
  | `ZYPHER_NETWORK` | `mainnet` | Hyperliquid network for perp reads (`mainnet` is the default and the product's only network). |
111
115
  | `ZYPHER_ADDRESS` | unset | Public `0x...` address used by `get_portfolio` / `get_hl_account` when no `address` argument is passed. Never put a private key here. |
112
116
  | `X_BEARER_TOKEN` | unset | Your X API bearer token, only for `get_social_info`. |
113
- | `ZYPHER_API_TOKEN` | unset | Agent Wallet API token (`zat_` + 40 lowercase hex). Enables the nine `agent_*` tools. Sent only as `Authorization: Bearer` to `ZYPHER_API_URL`; never logged. |
117
+ | `ZYPHER_API_TOKEN` | unset | Agent Wallet API token (`zat_` + 40 lowercase hex). Enables the ten `agent_*` tools and `upload_token_image`. Sent only as `Authorization: Bearer` to `ZYPHER_API_URL`; never logged. |
114
118
  | `ZYPHER_API_URL` | `https://zypheragent.com/agent` | Base URL of the Zypher Agent API. Must be https (plain http only for `localhost` / `127.0.0.1`). A different host prints a one-line stderr warning that the token will be sent there. |
115
119
  | `ELYSIUM_RPC` | `https://rpc-live.boxa.club` | Override the Elysium (99901) RPC URL. |
116
120
  | `HYPEREVM_RPC` | public HyperEVM RPC | Override the HyperEVM (999) RPC URL. |
@@ -119,7 +123,7 @@ No other variables are read.
119
123
 
120
124
  ## Network behaviour
121
125
 
122
- - Without a token only public, unauthenticated data endpoints are contacted. With `ZYPHER_API_TOKEN` it additionally talks to `ZYPHER_API_URL` (default `https://zypheragent.com/agent`), only on the nine fixed `/api/agent-api/*` paths. Read tools contact: the Hyperliquid info API (`api.hyperliquid.xyz`), the public HyperEVM RPC (`rpc.hyperliquid.xyz/evm`) and the Elysium 99901 RPC (`rpc-live.boxa.club`, run by Boxa) and, for the Elysium market tools, the public Zypher API (`zypheragent.com/agent`). `get_social_info` contacts `api.x.com` only with your own token.
126
+ - Without a token only public, unauthenticated data endpoints are contacted. With `ZYPHER_API_TOKEN` it additionally talks to `ZYPHER_API_URL` (default `https://zypheragent.com/agent`), only on the ten fixed `/api/agent-api/*` paths. Read tools contact: the Hyperliquid info API (`api.hyperliquid.xyz`), the public HyperEVM RPC (`rpc.hyperliquid.xyz/evm`) and the Elysium 99901 RPC (`rpc-live.boxa.club`, run by Boxa) and, for the Elysium market tools, the public Zypher API (`zypheragent.com/agent`). `get_social_info` contacts `api.x.com` only with your own token.
123
127
  - Requests carry the header `User-Agent: zypher-mcp/<version>`.
124
128
  - No telemetry, no analytics, no phone-home. The package writes nothing to disk.
125
129
  - Default is **mainnet** (real markets): Hyperliquid mainnet, HyperEVM 999 and Elysium 99901. **Elysium 99901 is a community-run chain served by Boxa (rpc-live.boxa.club), not the official Kinetiq mainnet**; `get_launch_info` checks that the chain id and the pinned contract code hashes still match and says so when they do not. Its RPC and contracts are third-party; a lying or lagging RPC cannot be detected from here.
package/dist/index.js CHANGED
@@ -574,6 +574,42 @@ async function elysiumToken(address) {
574
574
  const px = typeof j.hypeUsd === "number" ? j.hypeUsd : null;
575
575
  return { network: "Elysium 99901", hypeUsd: px, token: row(j.token ?? j, px), detail: j.info ?? null };
576
576
  }
577
+ var rowAct = (r) => ({ ...r });
578
+ async function elysiumActivity(address, limit, before) {
579
+ const j = await getJson("/api/market/elysium/activity", { address, limit, before });
580
+ return { network: "Elysium 99901", token: j.token ?? address, source: j.source, rows: (j.rows ?? []).slice(0, limit).map(rowAct), nextBefore: j.nextBefore ?? null, approxTime: j.approxTime ?? true, note: j.note ?? "Times are approximate (block based). Page backwards with before=nextBefore." };
581
+ }
582
+ async function elysiumHolders(address) {
583
+ const j = await getJson("/api/market/elysium/holders", { address });
584
+ return { network: "Elysium 99901", token: address, holdersTotal: j.holdersTotal ?? null, coverage: j.coverage ?? "recent activity only", top: (j.top ?? []).slice(0, 25), note: j.note ?? null };
585
+ }
586
+ var IMAGE_MAX_BYTES = 4 * 1024 * 1024;
587
+ function sniffImage(b) {
588
+ const at = (s, o = 0) => s.split("").every((c, i) => b[o + i] === c.charCodeAt(0));
589
+ if (b.length > 8 && b[0] === 137 && at("PNG", 1) && b[4] === 13 && b[5] === 10 && b[6] === 26 && b[7] === 10) return "image/png";
590
+ if (b.length > 3 && b[0] === 255 && b[1] === 216 && b[2] === 255) return "image/jpeg";
591
+ if (b.length > 12 && at("RIFF") && at("WEBP", 8)) return "image/webp";
592
+ if (b.length > 6 && at("GIF8") && (b[4] === 55 || b[4] === 57) && b[5] === 97) return "image/gif";
593
+ return null;
594
+ }
595
+ async function uploadTokenImage(imageBase64) {
596
+ const raw = imageBase64.trim().replace(/^data:[^,]*;base64,/i, "").replace(/\s+/g, "");
597
+ if (!/^[A-Za-z0-9+/]+={0,2}$/.test(raw) || raw.length < 16) throw new Error("imageBase64 must be a base64 encoded PNG, JPEG, GIF or WebP image.");
598
+ if (raw.length > Math.ceil(IMAGE_MAX_BYTES / 3) * 4 + 8) throw new Error("That image is too large. The limit is 4 MB.");
599
+ const bytes = Buffer.from(raw, "base64");
600
+ if (bytes.length > IMAGE_MAX_BYTES) throw new Error("That image is too large. The limit is 4 MB.");
601
+ const type = sniffImage(bytes);
602
+ if (!type) throw new Error("The data is not a PNG, JPEG, GIF or WebP image (checked by its first bytes).");
603
+ const r = await fetch(BASE() + "/api/launch-mainnet/upload", { method: "POST", headers: { "content-type": type, accept: "application/json" }, body: new Uint8Array(bytes), redirect: "error", signal: AbortSignal.timeout(3e4) });
604
+ const text = await r.text();
605
+ let j = null;
606
+ try {
607
+ j = JSON.parse(text);
608
+ } catch {
609
+ }
610
+ if (!r.ok || !j?.ok || typeof j.url !== "string" || !/^https:\/\//.test(j.url)) throw new Error(`Image upload refused (HTTP ${r.status}): ${cleanLabel(String(j?.error ?? "unexpected answer"), 160)}`);
611
+ return { url: j.url, type, bytes: bytes.length, next: "Pass this url as `image` to agent_elysium_launch." };
612
+ }
577
613
 
578
614
  // src/market/reads.ts
579
615
  import { formatUnits as formatUnits3, getAddress as getAddress4, isAddress as isAddress2, parseUnits } from "viem";
@@ -994,6 +1030,18 @@ var tools = [
994
1030
  shape: { address: evmAddr },
995
1031
  handler: async (a) => elysiumToken(a.address)
996
1032
  },
1033
+ {
1034
+ name: "get_elysium_activity",
1035
+ description: "Recent swaps (or transfers) of an Elysium 99901 token, newest first, read from chain logs: side, amounts, wallet, block. Times are approximate. Page backwards with before = nextBefore of the previous answer.",
1036
+ shape: { address: evmAddr, limit: z.number().int().min(1).max(60).default(30), before: z.number().int().min(0).max(1e9).optional() },
1037
+ handler: async (a) => elysiumActivity(a.address, a.limit, a.before)
1038
+ },
1039
+ {
1040
+ name: "get_elysium_holders",
1041
+ description: "Largest holders of an Elysium 99901 token SEEN IN RECENT ACTIVITY (not a full ranking), plus the total holder count from the index.",
1042
+ shape: { address: evmAddr },
1043
+ handler: async (a) => elysiumHolders(a.address)
1044
+ },
997
1045
  {
998
1046
  name: "analyze_token_health",
999
1047
  description: "Check a token contract (mint/pause/blacklist/owner) on HyperEVM mainnet (default) or Elysium 99901 and give a score with reasons. Bytecode heuristic, not a safety guarantee.",
@@ -1076,7 +1124,7 @@ var MAX_RESPONSE_BYTES = 100 * 1024;
1076
1124
  var TIMEOUT_MS = 15e3;
1077
1125
  var SWAP_TIMEOUT_MS = 45e3;
1078
1126
  var DEFAULT_HOST = "zypheragent.com";
1079
- var ALLOWED_PATHS = ["/api/agent-api/status", "/api/agent-api/swap", "/api/agent-api/elysium-swap", "/api/agent-api/elysium-launch", "/api/agent-api/bridge-boxa", "/api/agent-api/perp-order", "/api/agent-api/close", "/api/agent-api/send", "/api/agent-api/transfer-out"];
1127
+ var ALLOWED_PATHS = ["/api/agent-api/status", "/api/agent-api/swap", "/api/agent-api/elysium-swap", "/api/agent-api/elysium-launch", "/api/agent-api/bridge-boxa", "/api/agent-api/bridge", "/api/agent-api/perp-order", "/api/agent-api/close", "/api/agent-api/send", "/api/agent-api/transfer-out"];
1080
1128
  function loadExecConfig(e2 = process.env) {
1081
1129
  const token = e2.ZYPHER_API_TOKEN?.trim();
1082
1130
  if (!token) return { enabled: false };
@@ -1114,6 +1162,7 @@ var usd = (max) => z2.union([z2.string().regex(/^\d{1,12}(\.\d{1,8})?$/), z2.num
1114
1162
  return n3;
1115
1163
  });
1116
1164
  var tokenRef = z2.union([ticker, evmAddr]);
1165
+ var chainRef = z2.union([z2.string().regex(/^[A-Za-z0-9._-]{1,30}$/), z2.number().int().positive()]);
1117
1166
  var httpsUrl = z2.string().max(400).regex(/^https:\/\/\S+$/, "must be an https:// URL");
1118
1167
  var slippageBps = z2.number().int().min(1).max(5e3).optional();
1119
1168
  var HONEST = "REAL FUNDS, MAINNET: this executes IMMEDIATELY from the user's Agent Wallet, inside the caps set by the user/operator in the Zypher Agent app. There is NO confirmation step and this MCP never holds keys. Ask the human for explicit approval of the exact parameters BEFORE calling.";
@@ -1168,6 +1217,14 @@ var SPECS = [
1168
1217
  shape: { direction: z2.enum(["deposit", "withdraw"]), amount },
1169
1218
  description: `Move native HYPE between HyperEVM and ELYSIUM 99901 for the user's Agent Wallet (Boxa bridge): deposit = HyperEVM -> Elysium (needed before agent_elysium_swap / agent_elysium_launch), withdraw = Elysium -> HyperEVM (can take a while). ${HONEST}`
1170
1219
  },
1220
+ {
1221
+ name: "agent_bridge",
1222
+ method: "POST",
1223
+ path: "/api/agent-api/bridge",
1224
+ swap: true,
1225
+ shape: { originChain: chainRef, destinationChain: chainRef, originToken: z2.string().min(1).max(60).optional(), destinationToken: z2.string().min(1).max(70).optional(), amount, recipient: evmAddr.optional() },
1226
+ description: `Bridge funds with Relay between chains (any supported chain to any, e.g. ethereum -> hyperevm, arbitrum -> base) from the user's Agent Wallet. originChain/destinationChain = a chain name or numeric id; tokens by symbol or 0x address (default: the native token). With an API token the recipient can only be the Agent Wallet itself (leave it out) or an EVM address on the owner's send allow-list; Solana recipients are not possible. For HYPE between HyperEVM and Elysium use agent_bridge_elysium instead. ${HONEST}`
1227
+ },
1171
1228
  {
1172
1229
  name: "agent_perp_order",
1173
1230
  method: "POST",
@@ -1276,10 +1333,13 @@ function execTools(cfg) {
1276
1333
  }));
1277
1334
  }
1278
1335
 
1336
+ // src/index.ts
1337
+ import { z as z3 } from "zod";
1338
+
1279
1339
  // package.json
1280
1340
  var package_default = {
1281
1341
  name: "@zypheragent/mcp",
1282
- version: "0.4.0",
1342
+ version: "0.5.0",
1283
1343
  description: "MCP server of Zypher Agent: Hyperliquid perp analytics, HyperSwap quotes on HyperEVM and Elysium market and launchpad feed for AI assistants. Read-only by default; optional execution (swaps, Elysium swap and token launch, bridge, perp orders, close, send) only through a user-created Agent Wallet API token. This package holds no keys.",
1284
1344
  license: "MIT",
1285
1345
  author: "Zypher Agent",
@@ -1341,7 +1401,13 @@ globalThis.fetch = (input, init = {}) => {
1341
1401
  var execCfg = loadExecConfig();
1342
1402
  if (!execCfg.enabled && execCfg.reason) console.error(`zypher-mcp: ${execCfg.reason}`);
1343
1403
  if (execCfg.enabled && execCfg.warning) console.error(`zypher-mcp: ${execCfg.warning}`);
1344
- var tools2 = execCfg.enabled ? [...tools, ...execTools(execCfg)] : tools;
1404
+ var uploadTool = {
1405
+ name: "upload_token_image",
1406
+ description: "Upload a token image (PNG, JPEG, GIF or WebP, up to 4 MB, given as base64 or a data: URL) and get back an https URL for the `image` field of agent_elysium_launch. Uploads to Boxa's image host through the Zypher API; no funds are involved and your API token is NOT sent. Rate limited (6 per minute).",
1407
+ shape: { imageBase64: z3.string().min(16).max(56e5) },
1408
+ handler: async (a) => uploadTokenImage(a.imageBase64)
1409
+ };
1410
+ var tools2 = execCfg.enabled ? [...tools, ...execTools(execCfg), uploadTool] : tools;
1345
1411
  setExecEnabled(execCfg.enabled);
1346
1412
  var server = new McpServer({ name: "zypher-agent", version: package_default.version });
1347
1413
  for (const t of tools2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zypheragent/mcp",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "MCP server of Zypher Agent: Hyperliquid perp analytics, HyperSwap quotes on HyperEVM and Elysium market and launchpad feed for AI assistants. Read-only by default; optional execution (swaps, Elysium swap and token launch, bridge, perp orders, close, send) only through a user-created Agent Wallet API token. This package holds no keys.",
5
5
  "license": "MIT",
6
6
  "author": "Zypher Agent",