{
  "markdown": "# sui-mcp\n\n[![CI](https://github.com/0xfreak0/sui-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/0xfreak0/sui-mcp/actions/workflows/ci.yml)\n\nRead-only MCP server for **investigating activity on Sui**. Trace where funds went, attribute wallets to their funding sources, rank addresses by protocol flow, and tell a coordinated cluster from a crowd — then reconstruct it all on a timeline.\n\n62 tools. It also does the ordinary things well — wallet overviews, DeFi positions, NFTs, prices, Move package analysis — but the reason to pick this one is the forensics.\n\n## Install\n\nAdd this to your MCP client config — Claude Code, Claude Desktop, Cursor, or anything else that speaks MCP over stdio:\n\n```json\n{\n  \"mcpServers\": {\n    \"sui\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"sui-analytics-mcp\"]\n    }\n  }\n}\n```\n\nNo account, API key, or config file is required. The server reads public Sui endpoints and defaults to mainnet. Requires Node.js >= 22.13.\n\nDoing investigative work? Start with the forensics tools loaded:\n\n```json\n\"env\": { \"SUI_TOOLS\": \"core,forensics\" }\n```\n\n## What an investigation looks like\n\nRanking a lending protocol's wallets for a day, then testing whether a cluster is coordinated — six calls:\n\n```\naggregate_events(module: <package>, from: \"2026-08-07T00:00:00Z\", to: \"now\")\n  → every event type it emits, with counts and the numeric fields available\n    (user actions are usually far rarer than bookkeeping events)\n\naggregate_events(event_type: <DepositEvent>, value_field: \"event.deposit_value\", value_scale: 100)\n  → wallets ranked by USD deposited, truncated: false\n\nfind_funding_sources(addresses: [...25], depth: \"first_hop\")\n  → 23 of 25 share one funder, funded in three bursts of under a minute\n\nget_address_fanout(<that funder>)\n  → 1,623 recipients — \"distributor\", so co-funding alone proves nothing;\n    the second-level timing clustering is what carries it\n```\n\nThat last step is the point. Several wallets tracing to one funder looks decisive until you measure the funder. Every funding result carries that measurement so a coincidence doesn't get reported as a link.\n\nFan-out reports **shape as well as size**, because size alone doesn't separate the cases that matter. Measured on the same day, a known exchange and a sybil funder had almost identical counterparty counts — 399 and 431 — and completely different flow: the exchange ran balanced at 0.73 out/in (deposits in, withdrawals out) while the funder ran 9.78 (it pays many and is paid by few). One is noise in an investigation; the other is the thing you're looking for.\n\n## The forensics skill\n\nThe server gives Claude chain access. It does not, on its own, give it method —\nwhich tool answers which question, what a control group is for, or which\nconclusions to refuse. That lives in a skill shipped alongside it.\n\n```bash\nmkdir -p ~/.claude/skills\ncp -r \"$(npm root -g)/sui-analytics-mcp/.claude/skills/sui-forensics\" ~/.claude/skills/\n```\n\nOr copy `.claude/skills/sui-forensics/` out of this repo. It loads automatically\nonce present; there is nothing to configure.\n\nIt covers the evidence tiers and what each licenses you to claim, the order to\nwork in, the base-rate check that stops shared ancestry reading as collusion,\nand the conclusions to refuse — \"no edge found, so they are unrelated\" being the\none that costs most.\n\n## Tool profiles\n\nAll 62 tools loaded at once cost about 14k tokens of context on every request, and a large flat tool list makes models pick the wrong tool. So the server starts with a **core** set of 17 and keeps the rest one call away.\n\nWhen you ask for something outside the current set — \"trace where these funds went\" — the model calls `enable_tools` and the tracing tools appear immediately, no restart. You never have to pick a profile.\n\nTo start with more, set `SUI_TOOLS`:\n\n```json\n\"env\": { \"SUI_TOOLS\": \"core,forensics\" }\n```\n\n| Profile | Tools | Contents |\n|---|---|---|\n| `core` *(default)* | 18 | Wallets, balances, transactions (single and batched), tokens, NFTs, DeFi positions, staking, pools, names |\n| `forensics` | 24 | Fund tracing, funding-source attribution, cross-chain bridge resolution, wallet-edge clustering, package analysis, control-group sampling, timelines, object provenance, labels, events, oracle-vs-market deviation |\n| `developer` | 18 | Move packages, disassembly, decompilation, upgrade diffing, dependency graphs, PTB decoding, unsigned transaction building, Move Registry |\n| `market` | 6 | DeepBook order book and fills, pool stats, token search, validators |\n| `all` | 59 | Everything |\n\nRuntime switching relies on `notifications/tools/list_changed`. Claude Code and Claude Desktop honour it; some clients cache the tool list and will only see the change after a restart. `SUI_TOOLS` always works, so set it explicitly if your client doesn't refresh.\n\nUpgrading from 1.1.x, where every tool loaded at startup? Set `SUI_TOOLS=all` to keep that behaviour.\n\n## No wallet, no keys\n\nThe server has no credentials and no ability to move funds:\n\n- It never accepts a private key, mnemonic, or seed phrase. No tool takes one as an argument and nothing in the code reads one from the environment.\n- It never submits a transaction. `build_transfer` and `build_staking` return unsigned BCS bytes that you sign and broadcast somewhere else; `simulate_transaction` dry-runs bytes against a fullnode without executing them.\n- Every remaining tool is a read.\n- No provider accounts. RPC, indexing, and price data all come from public endpoints.\n\n### What the process actually does\n\nSupply-chain scanners report the capabilities a package uses, without the reason. Here is the full list for this one:\n\n| Capability | Where it's used |\n|---|---|\n| Network | Public Sui RPC and GraphQL, plus Pyth, Aftermath and the Move Registry for prices and name resolution. Hosts are listed in [`src/config.ts`](src/config.ts). |\n| Filesystem | Temp files for `decompile_module`, and reading `SUI_LABELS_FILE` if you set it. |\n| Subprocess | One call, in [`src/tools/decompiler.ts`](src/tools/decompiler.ts), to the decompiler binary *you* build and point at. `execFile` with array arguments, so no shell is involved and nothing is interpolated into a command string. |\n| Environment | The `SUI_`-prefixed variables in [`.env.example`](.env.example), plus two optional price-provider keys (`PYTH_API_KEY`, `CMC_API_KEY`). Nothing else is read. |\n\nThere is no `eval`, no dynamic `require`, no minified or obfuscated code, and no telemetry. Inputs that come from the chain are treated as untrusted: `decompile_module` validates module names before they reach a filesystem path, and bounds how many modules one call will process.\n\nMost of the dependency tree is the MCP SDK. This server speaks stdio only and imports just `server/mcp.js` and `server/stdio.js`, so the SDK's HTTP-transport dependencies are installed but never loaded.\n\n### Verifying a release\n\nReleases are published from CI with [npm provenance](https://docs.npmjs.com/generating-provenance-statements), so every tarball carries a signed attestation tying it to the commit and workflow run that produced it:\n\n```bash\nnpm audit signatures\n```\n\n## Capabilities\n\n- **Per-call network** — every tool takes an optional `network` arg (`mainnet` / `testnet` / `devnet`); query multiple networks in one session (e.g. compare a testnet value to mainnet). `SUI_NETWORK` sets only the default.\n- **Protocol-aware** — decodes transactions from Cetus, Suilend, NAVI, Scallop, Bluefin, DeepBook, and more into human-readable actions\n- **Incident investigation** — labeled fund tracing, batch funding attribution with fan-out controls, multi-address timelines, object provenance, PTB anomaly triage, oracle-vs-market deviation\n- **Move package analysis** — disassembly, heuristic risk scan, capability audit, and upgrade diffing, none of which need an external binary\n- **Multi-source architecture** — gRPC for low-latency reads, GraphQL for filtered queries, archive node fallback for historical data\n- **Price aggregation** — Aftermath Finance, Pyth oracles, and CoinGecko in a single unified interface\n- **Kiosk-aware** — resolves NFT ownership through Sui's kiosk system to actual wallet addresses\n- **Move Registry (MVR)** — resolves names like `@deepbook/core` to package addresses, and back\n\n## Configuration\n\nAll environment variables are optional. See [`.env.example`](.env.example) for the full list; the common ones are `SUI_NETWORK` (default network), `SUI_FULLNODE_URL` / `SUI_GRAPHQL_URL` (custom RPC endpoints), and `SUI_LABELS_FILE` (address attribution labels for fund tracing).\n\n### Price sources\n\nCurrent USD prices come from **Aftermath**, which is free and needs no key — that is the default path and it covers everything except historical pricing.\n\nTwo paid sources are opt-in and engage only when their key is set, so nobody is billed by accident and nothing degrades if you set neither:\n\n| Variable | Enables |\n|---|---|\n| `PYTH_API_KEY` | Historical prices (`get_token_prices` with `at`), oracle-vs-market comparison. Pyth's Hermes endpoint began requiring authentication for price *values*; feed discovery is still open. |\n| `CMC_API_KEY` | CoinMarketCap as an additional current-price source. Note it keys on ticker symbols, which are not unique on-chain, so it is only consulted for symbols already mapped to a coin type. |\n\nWithout a key, tools that need a paid source say so explicitly rather than returning a null price — a missing price and a price of zero are different claims.\n\n### Optional local store\n\nSet `SUI_STORE_PATH` to keep address labels and fan-out measurements across sessions. It uses Node's built-in `node:sqlite`, so it adds no dependency and no native build. Unset by default — nothing is written to disk unless you ask for it, which matters because an investigation store is a record of which addresses you looked at.\n\n```json\n\"env\": { \"SUI_STORE_PATH\": \"/Users/you/.local/share/sui-mcp/store.db\" }\n```\n\nFund traces are deliberately not cached: a trace is a function of your labels, so a stored one would silently disagree with a fresh run the moment a label changed.\n\n```json\n{\n  \"mcpServers\": {\n    \"sui\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"sui-analytics-mcp\"],\n      \"env\": { \"SUI_NETWORK\": \"testnet\" }\n    }\n  }\n}\n```\n\n## Move decompiler (optional)\n\n61 of the 62 tools need nothing beyond the install above. Only `decompile_module` requires an external binary, and there are lighter options before you reach for it:\n\n- `disassemble_module` returns Move bytecode assembly via the GraphQL endpoint.\n- `analyze_package` summarizes a package's API and runs a heuristic risk scan.\n- `diff_package_upgrade` diffs two versions of a package.\n\nUse the decompiler when you want higher-level, source-like Move output instead of bytecode.\n\nThe binary is Revela's `move-decompiler`, built from Rust. It is not bundled in the npm package because a published tarball could only carry one platform's build, so you compile it once yourself and point the server at it with `SUI_DECOMPILER_PATH`. This works the same whether you installed via npx or from source. You need a Rust toolchain ([rustup.rs](https://rustup.rs/)); the build takes a few minutes.\n\n```bash\ngit clone --depth 1 https://github.com/verichains/revela_sui.git\ncd revela_sui/external-crates/move\ncargo build --release --bin move-decompiler\n# binary lands at target/release/move-decompiler\n```\n\nThen add its absolute path to your client config:\n\n```json\n{\n  \"mcpServers\": {\n    \"sui\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"sui-analytics-mcp\"],\n      \"env\": {\n        \"SUI_DECOMPILER_PATH\": \"/absolute/path/to/revela_sui/external-crates/move/target/release/move-decompiler\"\n      }\n    }\n  }\n}\n```\n\nIf you already cloned this repo, `npm run build:decompiler` does the same clone and build and copies the result to `bin/move-decompiler`.\n\nWithout `SUI_DECOMPILER_PATH` the server falls back to looking for `move-decompiler` on `PATH`. Prefer the absolute path: desktop clients often launch servers with a minimal environment that doesn't include your shell's `PATH`, so a binary you can run in a terminal may still be invisible to the server. If it's found in neither place, `decompile_module` returns an error explaining how to fix it, and the other 56 tools are unaffected.\n\n## Running from source\n\nFor development, or to run a version you've modified:\n\n```bash\ngit clone https://github.com/0xfreak0/sui-mcp.git\ncd sui-mcp\nnpm install\nnpm run build\n```\n\nThen point your client at the build output instead of npx:\n\n```json\n{\n  \"mcpServers\": {\n    \"sui\": {\n      \"command\": \"node\",\n      \"args\": [\"/absolute/path/to/sui-mcp/dist/index.js\"]\n    }\n  }\n}\n```\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for the development and release workflow.\n\n## Tools (62)\n\n### Recommended Starting Points\n\n| Tool | Description |\n|---|---|\n| `identify_address` | Identify what a Sui address is: wallet, package, validator, or object |\n| `get_wallet_overview` | Comprehensive wallet overview: balances, SuiNS name, staking, kiosks, recent txs |\n| `get_transaction_history` | Decoded activity feed with protocol names and human-readable actions |\n| `analyze_token` | Full token analysis: metadata, price, 24h change, supply, top holders |\n\n### Chain & Network\n\n| Tool | Description |\n|---|---|\n| `get_chain_info` | Current chain ID, epoch, checkpoint height, timestamp, gas price |\n| `get_checkpoint` | Checkpoint details by sequence number or digest |\n\n### Objects\n\n| Tool | Description |\n|---|---|\n| `get_object` | Object by ID with type, owner, JSON content, and display metadata |\n| `list_owned_objects` | List objects owned by an address with optional type filter |\n| `list_dynamic_fields` | Dynamic fields of an object (tables, kiosk contents, etc.) |\n\n### Coins & Tokens\n\n| Tool | Description |\n|---|---|\n| `get_balance` | Balance of a coin type for an address (defaults to SUI) |\n| `get_coin_info` | Token metadata: name, symbol, decimals, description, supply |\n| `search_token` | Search tokens by name/symbol, with Aftermath Finance fallback |\n| `get_token_prices` | USD prices for tokens — current (Aftermath + Pyth), or historical via Pyth when `at` is set |\n\n### Transactions & Events\n\n| Tool | Description |\n|---|---|\n| `get_transactions` | Reads up to 50 transactions in ONE call given their digests — sender, timing, balance changes, Move calls, and events with decoded fields. Ten digests go from ten round trips to one. Malformed digests are rejected before the request, because the server refuses a whole batch over one bad key |\n| `get_transaction` | Transaction by digest with protocol-decoded actions |\n| `query_transactions` | Filter transactions by sender, address, object, or function |\n| `query_events` | Filter events by type, sender, module, or checkpoint range |\n\n### DeFi\n\n| Tool | Description |\n|---|---|\n| `get_defi_positions` | DeFi positions across Suilend, Cetus, NAVI, Scallop, Bluefin, Bucket |\n| `find_pools` | Discover liquidity pools by token pair (Cetus, DeepBook, Turbos) |\n| `get_pool_stats` | Pool reserves, fees, and prices for a given pool object ID (AMMs; see below for DeepBook) |\n\n### DeepBook\n\nDeepBook v3 is a central limit order book, so it has no reserves — depth, spread and traded price come from the [DeepBook indexer](https://docs.sui.io/standards/deepbookv3-indexer) rather than from a pool object. Mainnet and testnet only.\n\n| Tool | Description |\n|---|---|\n| `deepbook_orderbook` | Live bid/ask depth, spread, mid price and resting-liquidity imbalance. Omit `pool_name` to list pools. |\n| `deepbook_trades` | Recent fills with maker/taker balance manager IDs — attribute trading to an account during an incident window |\n| `compare_oracle_price` | (Security) Pyth oracle price vs the price DeepBook actually traded at, over a window — detects stale feeds, manipulation windows, and liquidations priced at levels the market never printed |\n\n### NFTs\n\n| Tool | Description |\n|---|---|\n| `list_nfts` | List NFTs owned by a wallet, including kiosk-stored NFTs |\n| `list_nft_collections` | Lightweight collection summary with counts |\n| `get_top_holders` | Top holders of an NFT collection or token |\n\n### Staking\n\n| Tool | Description |\n|---|---|\n| `get_validators` | List validators (stake, commission, voting power), or full detail for one when `address` is set |\n| `get_staking_summary` | Wallet's staking positions and pools |\n\n### Names\n\n| Tool | Description |\n|---|---|\n| `resolve_name` | SuiNS name resolution (forward and reverse) |\n\n### Move Registry (MVR)\n\nThe [Move Registry](https://www.moveregistry.com) maps human-readable package names like `@suins/core` or `@deepbook/core` to on-chain package addresses. Backed by `mainnet.mvr.mystenlabs.com/v1` (or `testnet.mvr...` when `SUI_NETWORK=testnet`).\n\n| Tool | Description |\n|---|---|\n| `mvr_resolve` | Resolve one or many MVR names → package IDs. Accepts version-pinned names like `@suins/core/3`. |\n| `mvr_reverse_resolve` | Reverse-lookup: package addresses → MVR names. Useful for enriching raw addresses anywhere. |\n| `mvr_get_package_info` | Full record for a name: metadata, version, package_address, package_info ID, git source. |\n| `mvr_search` | Browse / search the registry. Supports substring search, pagination, and an `is_linked` filter for published packages. |\n| `mvr_resolve_struct` | Resolve `@org/app::module::Type` → canonical type tag at the type's defining-package address. |\n\n**Typical flows:**\n\n- *\"What's the package for `@deepbook/core`?\"* → `mvr_resolve(['@deepbook/core'])` → `0x4874e1...`. Hand the address to `get_package` for module/function details.\n- *\"What is package `0xf22f…`?\"* → `mvr_reverse_resolve(['0xf22f…'])` → `@suins/core`.\n- *\"Find DeepBook-related packages\"* → `mvr_search('deepbook', limit=20, is_linked=true)` → paginated list.\n- *\"Pin to a specific version\"* → `mvr_resolve(['@suins/core/3'])` returns the v3 package address rather than the latest.\n\n### Packages (Developer)\n\n| Tool | Description |\n|---|---|\n| `get_package` | Move package modules, structs (with ordered fields), and functions |\n| `get_move_function` | Specific Move function signature and parameters |\n| `get_package_dependency_graph` | Package dependency analysis with recursive traversal |\n| `analyze_package` | Summarize a package's API + heuristic risk scan (no binary; accepts 0x id or MVR name) |\n| `disassemble_module` | Disassemble Move bytecode via GraphQL (no binary; accepts 0x id or MVR name) |\n| `decompile_module` | Decompile Move bytecode to source (requires decompiler binary) |\n| `diff_package_upgrade` | (Security) Diff two package versions to spot what an upgrade changed — malicious-upgrade / backdoor detection |\n\n### Transaction Building\n\n| Tool | Description |\n|---|---|\n| `build_transfer` | Build an unsigned transfer of SUI or any coin (auto coin selection); returns BCS for `simulate_transaction` |\n| `build_staking` | Build an unsigned stake/unstake transaction (`action: stake\\|unstake`) |\n| `simulate_transaction` | Dry-run a transaction to preview effects and gas cost |\n\n### Advanced\n\n| Tool | Description |\n|---|---|\n| `decode_ptb` | Decode a Programmable Transaction Block from BCS bytes |\n| `check_activity` | Monitor address or object for new activity since a checkpoint |\n\n### Incident Investigation\n\n| Tool | Description |\n|---|---|\n| `trace_funds` | Swap-aware, USD-valued multi-hop fund tracing that stops at labeled sinks (forward or backward) |\n| `resolve_bridge_transfer` | Follow funds across a bridge, in either direction. Resolves **Wormhole** (VAA identity `(emitter chain, emitter address, sequence)`), **Sui's native bridge** and **Circle CCTP** — the latter two carry the destination chain and recipient in their own events, so their far side needs no indexer at all. Detects **Mayan MCTP** and any package the registry types as a bridge. Inbound claims resolve to their origin chain and transfer id rather than being mistaken for exits. Every result is tiered: `chain-derived` trusts nobody, `indexer-attested` is a lead to confirm |\n| `find_funding_source` | Walk an address back to its funding source(s) for attribution; stops at labeled exchanges/bridges |\n| `find_funding_sources` | Same, for up to 100 addresses in one call — shares work across converging chains, reports shared funders with flow shape, addresses paid by one transaction (weighed against that transaction's full recipient count), subjects that funded each other, and sub-minute funding bursts |\n| `sample_control_addresses` | Draw a random, reproducible control group from the same protocol and window, so a cohort's rate can be compared against chance |\n| `resolve_protocol_packages` | Find which of a protocol's package versions are actually emitting now — the bundled registry is a decode map full of historical IDs, and querying one returns nothing |\n| `get_address_fanout` | How many distinct addresses a funder pays. Tells an exchange hot wallet apart from a real common origin |\n| `build_wallet_edges` | Finds addresses that may share an operator with the ones you give it, and shows the evidence. Shared first funder, direct funding, shared gas sponsor, or a third party paying both. Exchanges and relayers are measured and discarded first |\n| `save_finding` | Record a conclusion against a named case, so an investigation outlives its session |\n| `list_findings` | List findings in a case, or every case with its count |\n| `export_case` | Render a case as a Markdown report, highest-confidence findings first |\n| `delete_finding` | Retract a finding that turned out to be wrong |\n| `aggregate_events` | Rank wallets or event types by activity/value over a time window — \"top wallets on this protocol today\" in one call |\n| `build_timeline` | Merge multiple addresses' activity into one checkpoint-ordered, protocol-decoded timeline |\n| `trace_object_history` | Object provenance: version history + ownership transitions (who created/held an object when) |\n| `manage_labels` | Address-label registry (exchanges, bridges, mixers, malicious wallets) used by the tracing tools |\n| `diff_package_upgrade` | Diff two package versions to detect malicious upgrades / backdoors |\n\n## License\n\n[MIT](LICENSE)\n",
  "bytes": 22177,
  "sha": "f9ce657bcec5a9d80180c6041cff4fd8dd5bd625ccaa14ac16df53145513ff28",
  "repo_slug": "0xfreak0/sui-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_0xfreak0_sui_mcp_0971188e/readme"
}