@esseyonsui/sui-mcp
A Model Context Protocol server over @esseyonsui/sui-sdk for an agent that runs on its owner's own machine. Stdio
transport. Testnet and localnet only. 0.1.15 is this package's version (Node 22+). It keeps each loanMarkets entry's oraclePkg, feed and reader when ESSEY_PRIVATE is a saved GET /v1/private/config, and it accepts a numeric rateBps plus bare even-length hex for sealed on the commit tools. It installs two commands: essey-sui-mcp (the server) and essey-sui-init (link an agent to the account its owner approves once). ESSEY_DEPLOYMENT and ESSEY_PRIVATE accept GET /v1/config and GET /v1/private/config saved as files. ESSEY_SHIELDED_STATE is the mode-600 shielded seed file. essey_upkeep calls the SDK upkeep, which sizes a pool repay at the live curve. essey_loan_repay_v2 repays a public coin. The relayer has no repay_v2 kind. Install with the underscore override in
site/content/sdk/install.md. It depends on
@esseyonsui/[email protected] (site/content/sdk/install.md), whose buildPoolBidV2 / buildOfferV2 compose the finished
pool spend that essey_private_sale_pool_bid_v2 and essey_private_offer_v2 take, and whose bidV2Body /
loanOfferV2Body build the relayer bodies those tools' transactions correspond to. A cutover ESSEY_DEPLOYMENT sets oraclePkg to the Pyth package so essey_borrow calls
pyth_oracle::read there. 0.1.2 added the essey_private_* tools when ESSEY_PRIVATE is set.
Two kinds of tools, split by key
essey_*tools sign with the agent's own key, inside the limits its owner set on chain. A refusal by the chain comes back as an error result with the transaction digest and the abort's Move name.essey_owner_*tools never sign. They return{ kind: "unsigned", sender, txBytes, kindBytes, agentSigned: false }for the owner to sign in their own wallet or passkey device. The owner address and OwnerCaps come from the chain (the key-separation check), not from configuration.
Read: essey_status, essey_offers, essey_loans, essey_quote, essey_rooms, essey_warnings.
Agent (signed): essey_post_offer, essey_cancel_offer, essey_borrow, essey_repay, essey_refinance,
essey_add_collateral, essey_claim, essey_upkeep.
A borrower opens and settles a sealed room from their own wallet; no tool lets the agent key borrow through a room, and
EsseyAgent.execute, which every agent tool signs through, refuses a room open or settle with NOT_AGENT_CALL.
Owner (unsigned): essey_owner_setup, essey_owner_lender_rules, essey_owner_deposit, essey_owner_renew,
essey_owner_revoke, essey_owner_withdraw, essey_owner_collect_default, essey_owner_loan.
Amounts are whole-number strings in the coin's smallest unit (USDC: 1 USDC = "1000000"; SUI: 1 SUI =
"1000000000").
Configuration (environment)
| variable | |
|---|---|
ESSEY_NETWORK | localnet or testnet (required) |
ESSEY_DEPLOYMENT | path to the deployment JSON (required) |
ESSEY_AGENT_KEY_FILE | the agent's key file, mode 600 (required; the only key setting) |
ESSEY_TREASURY, ESSEY_AGENT_CAP | the agent's treasury and cap (together, or neither before setup) |
ESSEY_CASH_TREASURY, ESSEY_CASH_CAP | a borrower's loan-coin treasury and cap (together) |
ESSEY_GAS | coins (default) or address-balance |
ESSEY_BORROW_ALLOWANCE | a borrower's borrow allowance in the loan coin's smallest unit; essey_owner_deposit puts it in the rules it builds and takes no allowance input |
ESSEY_REPAY_LEAD_MS | upkeep repays an offer loan this long before it matures (default and floor: 1 hour) |
ESSEY_AUTO_TOP_UP | on or off (default): upkeep moves SUI from the SUI treasury into a loan near its safety line |
ESSEY_AUTO_CLAIM | on (default) or off: upkeep brings home what a closed loan left |
ESSEY_RPC_URL, ESSEY_GRAPHQL_URL | optional overrides, still checked against the network |
ESSEY_PRIVATE | path to the private-flow ids file (ids only, including loanMarkets). Set it to deployments/essey-private.testnet.json in this repo, or the object from testnetPrivateMcp(), and the server adds essey_private_*. loanMarkets carries the three live markets, USDC (Market<SUI, USDC>), USDSUI (Market<SUI, USDSUI>) and SUI (Market<USDSUI, SUI>): the ids GET /v1/private/config returns under loanMarkets.<coin>.id |
Any other ESSEY_* variable stops the server, and so does any name containing OWNER or any value holding a private
key (src/config.ts).
Private flows and the hosted relayer
With ESSEY_PRIVATE set, essey_private_settle and essey_private_repay return unsigned bytes (agentSigned: false). Do not sign them with the agent key, and do not POST txBytes to the relayer. The hosted service rebuilds those calls from kind=settle_relayed and kind=repay on POST /relay/<coin>. The field mapping, placeholder bodies, and the refusal for raw txBytes are in site/content/sdk/mcp/private-tools.md. There is no repay_relayed entry: private_room::repay is the relayed repayment.
When the saved /v1/private/config carries saleRoomV2 and privateLoanV2 (Sui testnet does since 2026-11-08), five
more tools bind: essey_private_offer_commit_v2, essey_private_offer_v2, essey_private_sale_pool_bid_v2,
essey_private_action_binding_v2 and essey_private_settle_v2. They build the sealed-settlement rooms' calls
(sale_room_v2, private_room_v2): a bid or offer is a Poseidon commitment plus a Seal ciphertext only the host can
open after close, and settle/finish carries a Groth16 proof of the selection, never the losing values. The relayer
kinds are bid_v2, loan_offer_v2 and settle_relayed_v2; their fields are mapped from the tools' arguments in
site/content/sdk/mcp/private-tools.md, and the SDK builds the bodies (bidV2Body, loanOfferV2Body,
settleRelayedV2Body, or buildPoolBidV2 / buildOfferV2 with the proof).
Refusing to run
On start the server connects, loads the key, and runs the SDK's key-separation check. If the agent's key owns its own
OwnerCap (or the OwnerCap is shared, frozen or unreadable) it writes the reason to stderr and exits 1 without serving
(src/main.ts). Every signing tool runs the check again before it signs.
Upkeep: what a hosted agent gets, run by your own agent
One pass (essey_upkeep, or upkeep() in the SDK) does what the hosted runner does for its agents, with the same
rules: a borrower repays an offer loan within the repay lead of its maturity and every loan by its repay-by (one hour
before the cash cap ends, or the owner's on-chain repay-by if earlier), but only once its cash treasury and cash cap
cover what the chain can draw; brings home what a liquidated, written-off or defaulted loan left; and tops up a loan
near its safety line from the SUI treasury. A lender's loan unpaid past maturity plus grace comes back as an
owner-action step with the owner's unsigned claim_default bytes: the agent cannot sign it. Every pass ends with the
warnings of essey_warnings. dryRun: true reports without signing.
To run it as a loop instead of calling the tool: essey-sui-mcp upkeep [--every <seconds>] [--passes <n>] [--dry-run]
(every 60 s until SIGINT by default) with the same environment. It prints one JSON line per step and one per pass.
With Claude
examples/claude.ts prints the claude mcp add line and the claude_desktop_config.json block for a localnet
borrower, then drives the server with the official MCP client the way Claude does.
0.1.15
0.1.15 is this package's version. It keeps the dependency on @esseyonsui/sui-sdk at 0.1.11. The SDK is not republished: npm's @esseyonsui/[email protected] matches this tree. npm's @esseyonsui/[email protected] was published from gitHead 42d8b6f5, before PR #54 and PR #56. 0.1.15 is that code plus those fixes: wallet-loan offers are read from the room's offer table, a v1 shielded bid carries the spend-bound room credential, and a withdraw or exit whose amount is 0 is refused (ZERO_WITHDRAW).
0.1.16
Not on npm yet. It depends on @esseyonsui/sui-sdk 0.1.12, also not on npm yet. essey_borrow, essey_repay and essey_refinance return loanId, essey_loans takes includeClosed, and essey_shielded_recover returns the note's coin (from SDK 0.1.12 / MCP 0.1.16).
Dependencies
None at runtime beyond the SDK. The server speaks the stdio subset of MCP it needs (src/protocol.ts): the official
@modelcontextprotocol/sdk 1.32.2 resolves to 95 packages (express, hono, jose, cors and more) that would run inside
the process holding the agent key. The official client is installed only in test-client/ (exact pins, own lockfile,
npm ci --ignore-scripts; none of the 95 packages has an install script) to prove interoperability:
npm run test:interop.