paralith-mcp 0.1.0 → 0.3.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
@@ -9,7 +9,13 @@ Solana and Sui. Public docs: `products/orderbook/app/src/docs/pages/ai/mcp.md`
9
9
 
10
10
  Read-only, no install: connect your MCP client to `https://mcp.paralithmarkets.com/mcp`.
11
11
 
12
- To play, trade and bridge, run it locally with a phrase made for the agent (Node 20+):
12
+ To play, trade and bridge, connect every agent app on this machine (Claude Code, Claude Desktop, Codex, Grok), each with its own new wallet:
13
+
14
+ ```bash
15
+ npx -y paralith-mcp@latest setup
16
+ ```
17
+
18
+ Or by hand, with a phrase made for the agent (Node 20+):
13
19
 
14
20
  ```bash
15
21
  claude mcp add paralith --env PARALITH_MNEMONIC_FILE=/path/to/agent-phrase.txt -- npx -y paralith-mcp@latest
@@ -30,7 +36,9 @@ PARALITH_MNEMONIC_FILE=phrase.txt node scripts/call.mjs wallet '{}' # call one
30
36
  npm test # also fails if dist/ or the tool reference is stale
31
37
  ```
32
38
 
33
- - `src/server.mjs`: stdio entry. No phrase means read-only.
39
+ - `src/server.mjs`: stdio entry. No phrase means read-only. `setup` runs `src/setup.mjs` instead.
40
+ - `src/setup.mjs`: one wallet per agent app, written into each app's MCP config; `src/agents.mjs` records their public addresses.
41
+ - `src/progress.mjs`: the `progress` tool, one read across game, venues, vaults, bridge and gas, for one agent or all.
34
42
  - `src/http.mjs`: hosted read-only endpoint, `POST /mcp`, `GET /health`.
35
43
  - `src/build.mjs`: `buildServer()` and the account tools.
36
44
  - `src/keys.mjs`: phrase to AO/Base/Solana/Sui signers.
package/dist/chains.json CHANGED
@@ -4,11 +4,11 @@
4
4
  "chainId": 8453,
5
5
  "rpc": "https://mainnet.base.org",
6
6
  "explorer": "https://basescan.org",
7
- "bridge": "0xCE13574d0Ca4A0dE9c5E5039fD2E9F6ec2593409",
8
- "deployBlock": 52295541,
7
+ "bridge": "0x871ae9cd37B76F9216076d775928C733687596e6",
8
+ "deployBlock": 52429067,
9
9
  "tokens": [
10
10
  {
11
- "address": "0x405c70597266BefB0504389E333F10785B7919a9",
11
+ "address": "0xce2ED346Ff3E537E2D99B47F5162b0A07431d9a4",
12
12
  "symbol": "TEST-USDC",
13
13
  "decimals": 6,
14
14
  "faucet": true
@@ -19,95 +19,132 @@
19
19
  "cluster": "mainnet-beta",
20
20
  "rpc": "https://api.mainnet-beta.solana.com",
21
21
  "explorer": "https://solscan.io",
22
- "programId": "thXGuX413NPvD7zPrSv658UmVEwdki2Tu3zhbU6LoiM",
22
+ "programId": "41aBnUWVghxSWboMKY2F1MhtAYhPMU83igKbfvtjVjcr",
23
23
  "deployed": true,
24
24
  "tokens": [
25
25
  {
26
- "mint": "2oJf5PQxWYjLfPQ5yTBt5XE34F1zoP1fHCeFwoPcrdDn",
26
+ "mint": "4CjGDd6atsPPe7YPukt3aaHAuXUfhsA9KPkgbgVrcGyq",
27
27
  "symbol": "TEST-USDC",
28
28
  "decimals": 6,
29
29
  "tokenProgram": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
30
30
  "faucet": true
31
31
  }
32
32
  ],
33
- "programIndex": 4
33
+ "programIndex": 5
34
34
  },
35
35
  "sui": {
36
36
  "network": "mainnet",
37
37
  "rpc": "https://fullnode.mainnet.sui.io:443",
38
38
  "graphql": "https://graphql.mainnet.sui.io/graphql",
39
39
  "explorer": "https://suivision.xyz",
40
- "package": "0x43e8e6eea948b92358e799d9f27042ce9d77cf160ad2736ab0f5fd852d1ffa24",
41
- "originalPackage": "0x43e8e6eea948b92358e799d9f27042ce9d77cf160ad2736ab0f5fd852d1ffa24",
42
- "bridge": "0x63e9050c72530fb577379f6b9d979aba3b84afeb83833cbe47a6972443fcd300",
43
- "faucet": "0x24cdc90a23c129fe3afcc2bbb5c8bd0a63f10dad06ca6c04e03c41eb1e955ac3",
40
+ "package": "0x647f58c931f3f9c48ada70a3f91bc554f8945bcad16967e02a0bd49fffe9aed6",
41
+ "originalPackage": "0x647f58c931f3f9c48ada70a3f91bc554f8945bcad16967e02a0bd49fffe9aed6",
42
+ "bridge": "0x270b73af9e6543d196ba7a3bc160f87aabdc662e8601af46ceb0d465ae103337",
43
+ "faucet": "0x4c549789110006fadc343790b770fffa314269bcbeafd4f48853c3bf3d9639ef",
44
44
  "tokens": [
45
45
  {
46
- "type": "0x43e8e6eea948b92358e799d9f27042ce9d77cf160ad2736ab0f5fd852d1ffa24::test_usdc::TEST_USDC",
46
+ "type": "0x647f58c931f3f9c48ada70a3f91bc554f8945bcad16967e02a0bd49fffe9aed6::test_usdc::TEST_USDC",
47
47
  "symbol": "TEST-USDC",
48
48
  "decimals": 6,
49
49
  "faucet": true
50
50
  }
51
51
  ],
52
- "adminCap": "0xbfade5103b48bde0bdb5dcc0b016bb9571a93f79895de542e8853f522166d404",
52
+ "adminCap": "0xdd6abffeb51098b05be212f89fe44e78cbef062edfeec6078b225f9e75267570",
53
53
  "deployed": true,
54
54
  "upgradeCap": "0xa1fb1dc179dcdc0579dca5b50a8d1162c1a3c40f531fb723f21de206c1ca78cd"
55
55
  },
56
- "relayers": {
57
- "base": "0xDaaefC6Fe9C820DB57E9be7C0b566800de11ac22",
58
- "solana": "9eVvxEPKgpQ4Y2BZV8BWVAeRHWWfSEJy3vrM2AFNzdQ2",
59
- "sui": "0x46bd8f40263f6e056262f82f13c61d0849d813a030cde4c5bd6a8dd768144e9f",
60
- "ao": "z2OMYtbWHqNZY01ehkN0tsepznlrZQW-6dXPQO5lxDM"
61
- },
62
56
  "deploy": {
63
57
  "sui": {
64
- "id": "0x43e8e6eea948b92358e799d9f27042ce9d77cf160ad2736ab0f5fd852d1ffa24",
65
- "version": "sha256:a1fa3e2c74ded1733922a0055ed98b964223253daa25386a1adcec5e84e1b280",
66
- "localHash": "sha256:a1fa3e2c74ded1733922a0055ed98b964223253daa25386a1adcec5e84e1b280",
67
- "deployedAt": "2026-10-07T13:59:55.943Z",
68
- "tx": "2ENtda5acLVU3RyTfTC4U9n56C9F4wNJfhPK8fZq7FtB",
58
+ "id": "0x647f58c931f3f9c48ada70a3f91bc554f8945bcad16967e02a0bd49fffe9aed6",
59
+ "version": "sha256:d8675a23dc00cfe0d9e73f128ed5948d05e9a708afc1a68829906bec0aaeb432",
60
+ "localHash": "sha256:d8675a23dc00cfe0d9e73f128ed5948d05e9a708afc1a68829906bec0aaeb432",
61
+ "deployedAt": "2026-10-10T16:08:52.325Z",
62
+ "tx": "7CjZL9MGDVXUgSeGCNmT69HWtksnNyJaacFDTrAGahhu",
69
63
  "action": "FRESH"
70
64
  },
71
65
  "pusd": {
72
- "id": "sXuny0tvUizug41AHbbvWfDcjkqK0MEjrWSBTQwp3ac",
73
- "version": "sha256:4371b32bcc00fd14e85e740ac0f09d41bb6192509bee171d83594c901185eabc",
74
- "localHash": "sha256:4371b32bcc00fd14e85e740ac0f09d41bb6192509bee171d83594c901185eabc",
75
- "deployedAt": "2026-10-09T21:40:48.119Z",
76
- "tx": "sXuny0tvUizug41AHbbvWfDcjkqK0MEjrWSBTQwp3ac",
66
+ "id": "xhYkQ8qm7zssDC2nrOfzkk2Xy8rPVvlEMWestfrAvK8",
67
+ "version": "sha256:5c7aee2fad4dd8c558bbbcc4cf1dd19da28c5faf834bf4a801cf82969c0225c8",
68
+ "localHash": "sha256:5c7aee2fad4dd8c558bbbcc4cf1dd19da28c5faf834bf4a801cf82969c0225c8",
69
+ "deployedAt": "2026-10-10T15:51:14.512Z",
70
+ "tx": "xhYkQ8qm7zssDC2nrOfzkk2Xy8rPVvlEMWestfrAvK8",
77
71
  "action": "FRESH"
78
72
  },
79
73
  "base": {
80
- "id": "0xCE13574d0Ca4A0dE9c5E5039fD2E9F6ec2593409",
81
- "version": "0x85ba7d3498f2cf2c87849ee1d4966867465431d233837f1158964dece0af9028",
82
- "localHash": "0x85ba7d3498f2cf2c87849ee1d4966867465431d233837f1158964dece0af9028",
83
- "deployedAt": "2026-10-07T13:41:12.236Z",
84
- "tx": "0xb07dcc2967c1f803f7177ce99359f235319771fa0037982d983d1fd586bfdfef",
85
- "action": "FRESH"
74
+ "id": "0x871ae9cd37B76F9216076d775928C733687596e6",
75
+ "version": "0xe9e86188e3c0aa76ab1148c6cdee756045799152b0d796c2c74138ffae8ad03b",
76
+ "localHash": "0xe9e86188e3c0aa76ab1148c6cdee756045799152b0d796c2c74138ffae8ad03b",
77
+ "baselined": false,
78
+ "recordedAt": "2026-10-10T16:09:39.076Z",
79
+ "deployedAt": null
86
80
  },
87
81
  "solana": {
88
- "id": "thXGuX413NPvD7zPrSv658UmVEwdki2Tu3zhbU6LoiM",
89
- "version": "sha256:3ab07b185fa24f3da2a57352b9e2eed5f37475752cb2258d182d8a61afab7d2b",
90
- "localHash": "sha256:3ab07b185fa24f3da2a57352b9e2eed5f37475752cb2258d182d8a61afab7d2b",
91
- "deployedAt": "2026-10-07T13:59:36.619Z",
92
- "tx": "4gzQJeXcj6WZyhTP1djC95LVAsEiH78gVcNBk57KESYkwzWqRNDkkGgotQwENYEXfzqZpfRFcmysrBoHh44TMxz1",
82
+ "id": "41aBnUWVghxSWboMKY2F1MhtAYhPMU83igKbfvtjVjcr",
83
+ "version": "sha256:4279831e73a9247928ad5c4490bb203bc498d834da24f6da0ff20562898ec47e",
84
+ "localHash": "sha256:4279831e73a9247928ad5c4490bb203bc498d834da24f6da0ff20562898ec47e",
85
+ "deployedAt": "2026-10-10T16:08:35.744Z",
86
+ "tx": "J8trVshLfVhCxSJHnXXLhxdbkmkWLEhpE9FEjTHDK8qTkYY7izhX2zAtVscsztaTn7nMDnLhT88BHBw9MsVmbVC",
93
87
  "action": "FRESH"
94
88
  },
95
89
  "lastRun": {
96
- "at": "2026-10-09T21:41:00.279Z",
97
- "startedAt": "2026-10-09T19:17:15.931Z",
90
+ "at": "2026-10-10T16:09:39.077Z",
91
+ "startedAt": "2026-10-10T15:51:02.462Z",
98
92
  "steps": [
99
93
  {
100
94
  "component": "pusd",
101
95
  "action": "FRESH",
102
96
  "status": "done",
103
- "tx": "sXuny0tvUizug41AHbbvWfDcjkqK0MEjrWSBTQwp3ac"
97
+ "tx": "xhYkQ8qm7zssDC2nrOfzkk2Xy8rPVvlEMWestfrAvK8"
98
+ },
99
+ {
100
+ "component": "base",
101
+ "action": "FRESH",
102
+ "status": "done"
103
+ },
104
+ {
105
+ "component": "solana",
106
+ "action": "FRESH",
107
+ "status": "done",
108
+ "tx": "J8trVshLfVhCxSJHnXXLhxdbkmkWLEhpE9FEjTHDK8qTkYY7izhX2zAtVscsztaTn7nMDnLhT88BHBw9MsVmbVC"
109
+ },
110
+ {
111
+ "component": "sui",
112
+ "action": "FRESH",
113
+ "status": "done",
114
+ "tx": "7CjZL9MGDVXUgSeGCNmT69HWtksnNyJaacFDTrAGahhu"
104
115
  },
105
116
  {
106
117
  "component": "tokens",
107
118
  "action": "CONFIGURE",
108
119
  "status": "done"
120
+ },
121
+ {
122
+ "component": "graph",
123
+ "action": "CONFIGURE",
124
+ "status": "done"
125
+ },
126
+ {
127
+ "component": "site",
128
+ "action": "CONFIGURE",
129
+ "status": "done"
109
130
  }
110
131
  ]
111
132
  }
133
+ },
134
+ "attestors": [
135
+ {
136
+ "ao": "tkqULqMDeHKXCN79Cm_bDIV560JxXl6pBSmdPgxqwSw",
137
+ "operator": "efH9MDGEFZqQ4nTTRNxl8WXJi2NbZvd2xVgCWmG5EeM",
138
+ "base": "0xcefcaae4d5077eda2b126e9b315b5d7c4dd2ff38",
139
+ "solana": "BBuQ11wQE6K5bj6Cx7X5VpVDB9CG83nbeDdDSinavUz",
140
+ "sui": "0x397c399eb2f7e24e8f5d1988f71284baac48149b9e8cdcd741b353c771118ebc",
141
+ "epoch": 0
142
+ }
143
+ ],
144
+ "retiredRelayers": {
145
+ "base": "0xDaaefC6Fe9C820DB57E9be7C0b566800de11ac22",
146
+ "solana": "9eVvxEPKgpQ4Y2BZV8BWVAeRHWWfSEJy3vrM2AFNzdQ2",
147
+ "sui": "0x46bd8f40263f6e056262f82f13c61d0849d813a030cde4c5bd6a8dd768144e9f",
148
+ "ao": "z2OMYtbWHqNZY01ehkN0tsepznlrZQW-6dXPQO5lxDM"
112
149
  }
113
150
  }
package/dist/client.mjs CHANGED
@@ -401,7 +401,7 @@ var init_slot_settle = __esm({
401
401
  var graph_default;
402
402
  var init_graph = __esm({
403
403
  "runerealm-graph:C:\\REPO\\paralith-mono\\products\\runerealm-ui\\src\\lib\\graph.json"() {
404
- graph_default = { node: "https://hyperbeam.tylerw.ai", owner: "DA9qhP25ZPz6MHIhO-7aNHDN3LsTAL7yCKYIkqr13Z8", deployedAt: "", processes: { game: "bqkZT1qY8Qpea7QsKZEk8q7m0yUE8-G3Miq8EFXnRJg", rune: "0j4rkkEqUO25aXI8LoDuou2Ibr0GSMGsXrFRxwrSfY8", pusd: "sXuny0tvUizug41AHbbvWfDcjkqK0MEjrWSBTQwp3ac", dao: "YAPgWNxRQksjYn_zfUw-h-JkwpmJDtPL1K9yifR5S_Q", elements: { earth: "3CS-LPEfD4YJwpuh1kuPGOv3lGwIlOAWOT15ATGIbPg", air: "y51gmVYD1GoJWtMxAtePMnvMCR0aT-jSfnsMKjFeU70", fire: "os6EKVQGuukvgTFmaZbGj23Xt3frHB3cb8ljquCdJ18", water: "P9OPPoVw9MxpIz8QghA6TcYUhXrYG3Ro09KM8shX5QM" }, internalVenue: "pxw-Y9Hl8CNQlPGLr-2Ez_9BVjjEP6wisxrZhe-AyxU", externalVenue: "D3SUysjoweBXtoGFm4zoq1dKSt7Dc5b_V9x_weLJV7Y", hunt: "4iABQxFjL3LtMGgfZQuwsH5WHKKjjSu7b7XfxEN_j40", huntWorkers: ["4iABQxFjL3LtMGgfZQuwsH5WHKKjjSu7b7XfxEN_j40", "2RBkmldqPdij2PopnEPuStLl6Pb6OcIrpilfK64Q1m8", "EATa5vTV53J1c711CEjiuks2Kllq3Gi-kVSXFqe7O7M", "SHXyU87725eI8i6qSET70Pwovf744DJqEfajTZI4N4M", "iesbJSITvqtDXMHxhGeOOXUXofnD-nh1qro9PH5IHNQ"], battleWorkers: ["Xk8XXWY2QY-UCd1Y_yqx6SHlVt5bNUbQnwzngMgxbzc", "IPjes-iFyMZH_JCx1z-4c-7JEdJgd3RGgbglgl3_u2s", "-m-6qgUsU6ybjOpMMlPHvG1bIlHypFMORKw4blCpRnU", "mSzalVGuEgxAG_tCEhVivSPMhSvmp5ePMHRIW0-gASM", "PkPEU-yZpgn9LwXWYPahUKOPe-Hw2sHB0DPBNPoxQeA"] }, huntNode: "https://hyperbeam.tylerw.ai", marketNode: "https://hyperbeam.tylerw.ai", venueNode: "https://hyperbeam.tylerw.ai" };
404
+ graph_default = { node: "https://hyperbeam.tylerw.ai", owner: "DA9qhP25ZPz6MHIhO-7aNHDN3LsTAL7yCKYIkqr13Z8", deployedAt: "", processes: { game: "bqkZT1qY8Qpea7QsKZEk8q7m0yUE8-G3Miq8EFXnRJg", rune: "0j4rkkEqUO25aXI8LoDuou2Ibr0GSMGsXrFRxwrSfY8", pusd: "xhYkQ8qm7zssDC2nrOfzkk2Xy8rPVvlEMWestfrAvK8", dao: "YAPgWNxRQksjYn_zfUw-h-JkwpmJDtPL1K9yifR5S_Q", elements: { earth: "3CS-LPEfD4YJwpuh1kuPGOv3lGwIlOAWOT15ATGIbPg", air: "y51gmVYD1GoJWtMxAtePMnvMCR0aT-jSfnsMKjFeU70", fire: "os6EKVQGuukvgTFmaZbGj23Xt3frHB3cb8ljquCdJ18", water: "P9OPPoVw9MxpIz8QghA6TcYUhXrYG3Ro09KM8shX5QM" }, internalVenue: "pxw-Y9Hl8CNQlPGLr-2Ez_9BVjjEP6wisxrZhe-AyxU", externalVenue: "D3SUysjoweBXtoGFm4zoq1dKSt7Dc5b_V9x_weLJV7Y", hunt: "4iABQxFjL3LtMGgfZQuwsH5WHKKjjSu7b7XfxEN_j40", huntWorkers: ["4iABQxFjL3LtMGgfZQuwsH5WHKKjjSu7b7XfxEN_j40", "2RBkmldqPdij2PopnEPuStLl6Pb6OcIrpilfK64Q1m8", "EATa5vTV53J1c711CEjiuks2Kllq3Gi-kVSXFqe7O7M", "SHXyU87725eI8i6qSET70Pwovf744DJqEfajTZI4N4M", "iesbJSITvqtDXMHxhGeOOXUXofnD-nh1qro9PH5IHNQ"], battleWorkers: ["Xk8XXWY2QY-UCd1Y_yqx6SHlVt5bNUbQnwzngMgxbzc", "IPjes-iFyMZH_JCx1z-4c-7JEdJgd3RGgbglgl3_u2s", "-m-6qgUsU6ybjOpMMlPHvG1bIlHypFMORKw4blCpRnU", "mSzalVGuEgxAG_tCEhVivSPMhSvmp5ePMHRIW0-gASM", "PkPEU-yZpgn9LwXWYPahUKOPe-Hw2sHB0DPBNPoxQeA"] }, huntNode: "https://hyperbeam.tylerw.ai", marketNode: "https://hyperbeam.tylerw.ai", venueNode: "https://hyperbeam.tylerw.ai" };
405
405
  }
406
406
  });
407
407
 
@@ -9801,9 +9801,12 @@ var ShardedVenue = class {
9801
9801
  return { cancelled: { cancelledIds }, account: await this.summed(address, targets) };
9802
9802
  }
9803
9803
  /** Housekeeping, on every pair: anyone may call it, nobody is paid to. */
9804
- async maintain(limit = 25) {
9804
+ /** `pairIds` sweeps only those pairs: one signed message each, so name the ones holding expired orders. */
9805
+ async maintain(limit = 25, pairIds) {
9805
9806
  let expired = 0;
9806
- for (const pair2 of Object.values(await this.pairs())) {
9807
+ const pairs = await this.pairs();
9808
+ const due = pairIds ? pairIds.filter((id) => pairs[id]).map((id) => pairs[id]) : Object.values(pairs);
9809
+ for (const pair2 of due) {
9807
9810
  const reply = unwrap4(await this.transport.send(pair2.process, tags2({
9808
9811
  Action: "Order.Maintain",
9809
9812
  Limit: String(Math.max(1, Math.floor(limit)))
@@ -10016,9 +10019,9 @@ var cancelAllVenueOrders = async (process, item) => {
10016
10019
  ...item ? { Item: item } : {}
10017
10020
  });
10018
10021
  };
10019
- var maintainVenueOrders = async (process, limit = 25) => {
10022
+ var maintainVenueOrders = async (process, limit = 25, pairIds) => {
10020
10023
  const s = await shard(process);
10021
- if (s) return s.maintain(limit);
10024
+ if (s) return s.maintain(limit, pairIds);
10022
10025
  return write4(process, {
10023
10026
  Action: "Order.Maintain",
10024
10027
  Limit: String(Math.max(1, Math.floor(limit)))
package/dist/graph.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "node": "https://hyperbeam.tylerw.ai",
3
3
  "owner": "DA9qhP25ZPz6MHIhO-7aNHDN3LsTAL7yCKYIkqr13Z8",
4
- "deployedAt": "2026-10-09T21:40:15.402Z",
4
+ "deployedAt": "2026-10-10T15:51:09.959Z",
5
5
  "processes": {
6
6
  "game": "bqkZT1qY8Qpea7QsKZEk8q7m0yUE8-G3Miq8EFXnRJg",
7
7
  "rune": "0j4rkkEqUO25aXI8LoDuou2Ibr0GSMGsXrFRxwrSfY8",
8
- "pusd": "sXuny0tvUizug41AHbbvWfDcjkqK0MEjrWSBTQwp3ac",
8
+ "pusd": "xhYkQ8qm7zssDC2nrOfzkk2Xy8rPVvlEMWestfrAvK8",
9
9
  "dao": "YAPgWNxRQksjYn_zfUw-h-JkwpmJDtPL1K9yifR5S_Q",
10
10
  "elements": {
11
11
  "earth": "3CS-LPEfD4YJwpuh1kuPGOv3lGwIlOAWOT15ATGIbPg",
package/dist/http.mjs CHANGED
@@ -5,8 +5,8 @@ import http2 from "node:http";
5
5
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
6
6
 
7
7
  // src/runtime.mjs
8
- import fs2 from "node:fs";
9
- import path2 from "node:path";
8
+ import fs3 from "node:fs";
9
+ import path3 from "node:path";
10
10
  import { fileURLToPath, pathToFileURL } from "node:url";
11
11
 
12
12
  // ../../backend/native/ans104.mjs
@@ -135,7 +135,7 @@ import { mnemonicToAccount } from "viem/accounts";
135
135
  import { Keypair } from "@solana/web3.js";
136
136
  import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
137
137
  import { getKeyFromMnemonic } from "arweave-mnemonic-keys";
138
- var CACHE_DIR = process.env.PARALITH_MCP_HOME || path.join(os.homedir(), ".paralith", "mcp");
138
+ var HOME = process.env.PARALITH_MCP_HOME || path.join(os.homedir(), ".paralith", "mcp");
139
139
  function readPhrase(env = process.env) {
140
140
  const raw = env.PARALITH_MNEMONIC ?? (env.PARALITH_MNEMONIC_FILE ? fs.readFileSync(env.PARALITH_MNEMONIC_FILE, "utf8") : null);
141
141
  if (!raw) {
@@ -162,25 +162,27 @@ function slip10(seed, derivation) {
162
162
  async function aoJwk(phrase, env) {
163
163
  if (env.PARALITH_AO_JWK) return JSON.parse(fs.readFileSync(env.PARALITH_AO_JWK, "utf8"));
164
164
  const id = crypto2.createHash("sha256").update(phrase).digest("hex").slice(0, 24);
165
- const file = path.join(CACHE_DIR, `ao-${id}.json`);
165
+ const file = path.join(HOME, `ao-${id}.json`);
166
166
  if (fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, "utf8"));
167
167
  const jwk = await getKeyFromMnemonic(phrase);
168
- fs.mkdirSync(CACHE_DIR, { recursive: true, mode: 448 });
168
+ fs.mkdirSync(HOME, { recursive: true, mode: 448 });
169
169
  fs.writeFileSync(file, JSON.stringify(jwk), { mode: 384 });
170
170
  return jwk;
171
171
  }
172
- async function deriveWallets(phrase, env = process.env) {
172
+ function chainWallets(phrase) {
173
173
  const evm = mnemonicToAccount(phrase, { path: "m/44'/60'/0'/0/0" });
174
174
  const solana = Keypair.fromSeed(slip10(mnemonicToSeedSync(phrase), "m/44'/501'/0'/0'"));
175
175
  const sui = Ed25519Keypair.deriveKeypair(phrase, "m/44'/784'/0'/0'/0'");
176
- const jwk = await aoJwk(phrase, env);
177
176
  return {
178
- ao: { jwk, address: jwkToAddress(jwk) },
179
177
  base: { account: evm, address: evm.address },
180
178
  solana: { keypair: solana, address: solana.publicKey.toBase58() },
181
179
  sui: { keypair: sui, address: sui.getPublicKey().toSuiAddress() }
182
180
  };
183
181
  }
182
+ async function deriveWallets(phrase, env = process.env) {
183
+ const jwk = await aoJwk(phrase, env);
184
+ return { ao: { jwk, address: jwkToAddress(jwk) }, ...chainWallets(phrase) };
185
+ }
184
186
  var publicAddresses = (w) => ({
185
187
  ao: w.ao.address,
186
188
  base: w.base.address,
@@ -188,12 +190,32 @@ var publicAddresses = (w) => ({
188
190
  sui: w.sui.address
189
191
  });
190
192
 
193
+ // src/agents.mjs
194
+ import fs2 from "node:fs";
195
+ import path2 from "node:path";
196
+ var FILE = path2.join(HOME, "agents.json");
197
+ function readAgents() {
198
+ try {
199
+ return JSON.parse(fs2.readFileSync(FILE, "utf8"));
200
+ } catch {
201
+ return {};
202
+ }
203
+ }
204
+ function saveAgent(name, entry) {
205
+ const all = readAgents();
206
+ all[name] = { ...all[name], ...entry, addresses: { ...all[name]?.addresses, ...entry.addresses } };
207
+ fs2.mkdirSync(HOME, { recursive: true, mode: 448 });
208
+ fs2.writeFileSync(FILE, `${JSON.stringify(all, null, 2)}
209
+ `);
210
+ return all[name];
211
+ }
212
+
191
213
  // src/runtime.mjs
192
- var HERE = path2.dirname(fileURLToPath(import.meta.url));
193
- var DIST = fs2.existsSync(path2.join(HERE, "graph.json")) ? HERE : path2.join(HERE, "..", "dist");
194
- var graph = JSON.parse(fs2.readFileSync(path2.join(DIST, "graph.json"), "utf8"));
214
+ var HERE = path3.dirname(fileURLToPath(import.meta.url));
215
+ var DIST = fs3.existsSync(path3.join(HERE, "graph.json")) ? HERE : path3.join(HERE, "..", "dist");
216
+ var graph = JSON.parse(fs3.readFileSync(path3.join(DIST, "graph.json"), "utf8"));
195
217
  var NODE = graph.node.replace(/\/$/, "");
196
- var chains = JSON.parse(fs2.readFileSync(path2.join(DIST, "chains.json"), "utf8"));
218
+ var chains = JSON.parse(fs3.readFileSync(path3.join(DIST, "chains.json"), "utf8"));
197
219
  var PROCESS_ALIASES = {
198
220
  game: graph.processes.game,
199
221
  rune: graph.processes.rune,
@@ -215,7 +237,8 @@ var loaded = null;
215
237
  function runtime() {
216
238
  loaded ??= (async () => {
217
239
  const wallets = READ_ONLY ? null : await deriveWallets(readPhrase());
218
- const api = await import(pathToFileURL(path2.join(DIST, "client.mjs")).href);
240
+ if (wallets && process.env.PARALITH_AGENT) saveAgent(process.env.PARALITH_AGENT, { addresses: publicAddresses(wallets) });
241
+ const api = await import(pathToFileURL(path3.join(DIST, "client.mjs")).href);
219
242
  if (wallets) api.swarmWalletSlot.wallet = walletShim(wallets.ao.jwk);
220
243
  return { api, wallets };
221
244
  })();
@@ -277,7 +300,7 @@ async function until(what, read, done, timeoutMs = 6e5, everyMs = 3e3) {
277
300
 
278
301
  // src/build.mjs
279
302
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
280
- import { z as z7 } from "zod";
303
+ import { z as z9 } from "zod";
281
304
 
282
305
  // src/tool.mjs
283
306
  import { z } from "zod";
@@ -1153,6 +1176,19 @@ function suiChain(w) {
1153
1176
  }
1154
1177
  };
1155
1178
  }
1179
+ async function gasOf(name, address) {
1180
+ if (name === "base") {
1181
+ const rpc = process.env.PARALITH_BASE_RPC || chains.base.rpc;
1182
+ const pub = createPublicClient({ chain: { ...base, id: chains.base.chainId }, transport: http(rpc) });
1183
+ return `${formatEther(await pub.getBalance({ address }))} ETH`;
1184
+ }
1185
+ if (name === "solana") {
1186
+ const conn = new Connection(process.env.PARALITH_SOLANA_RPC || chains.solana.rpc, "confirmed");
1187
+ return `${await conn.getBalance(new PublicKey(address)) / 1e9} SOL`;
1188
+ }
1189
+ const client = new SuiGrpcClient({ network: chains.sui.network, baseUrl: process.env.PARALITH_SUI_RPC || chains.sui.rpc });
1190
+ return `${Number((await client.getBalance({ owner: address })).balance.balance) / 1e9} SUI`;
1191
+ }
1156
1192
  async function chainFor(name) {
1157
1193
  const { wallets } = await runtime();
1158
1194
  return { base: baseChain, solana: solanaChain, sui: suiChain }[name](wallets);
@@ -1420,11 +1456,11 @@ function registerDao(server) {
1420
1456
  exec_process: z5.string().regex(ID).optional().describe("Process the passed proposal sends its one message to."),
1421
1457
  exec_tags: z5.record(z5.string(), z5.string()).optional().describe('Tags of that message, e.g. {"Action": "Admin.SetRules", ...}. At most 16; required with exec_process.')
1422
1458
  },
1423
- async ({ title, body, link, exec_process, exec_tags }) => send({
1459
+ async ({ title, body, link: link2, exec_process, exec_tags }) => send({
1424
1460
  Action: "Propose",
1425
1461
  Title: title,
1426
1462
  Body: body,
1427
- Link: link,
1463
+ Link: link2,
1428
1464
  "Exec-Process": exec_process,
1429
1465
  "Exec-Tags": exec_tags && JSON.stringify(exec_tags)
1430
1466
  }),
@@ -1626,6 +1662,175 @@ function registerVaults(server) {
1626
1662
  );
1627
1663
  }
1628
1664
 
1665
+ // src/show.mjs
1666
+ import fs4 from "node:fs";
1667
+ import path4 from "node:path";
1668
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
1669
+ import { z as z7 } from "zod";
1670
+ import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
1671
+ var GAME = (process.env.PARALITH_GAME_URL || "https://game.paralithmarkets.com").replace(/\/$/, "");
1672
+ var TRADE = (process.env.PARALITH_TRADE_URL || "https://trade.paralithmarkets.com").replace(/\/$/, "");
1673
+ var URI = "ui://paralith/view";
1674
+ var HERE2 = path4.dirname(fileURLToPath2(import.meta.url));
1675
+ function viewHtml() {
1676
+ for (const file of [path4.join(HERE2, "view.html"), path4.join(HERE2, "..", "dist", "view.html")]) {
1677
+ if (fs4.existsSync(file)) return fs4.readFileSync(file, "utf8");
1678
+ }
1679
+ throw new Error("view.html is missing: run npm run build");
1680
+ }
1681
+ var link = (base2, page, params) => {
1682
+ const query = new URLSearchParams(Object.entries(params).filter(([, v]) => v !== void 0 && v !== ""));
1683
+ return `${base2}/embed/${page}${query.size ? `?${query}` : ""}`;
1684
+ };
1685
+ var VIEWS = {
1686
+ battle: (a) => a.battle_id && a.worker ? link(GAME, "battle", { w: a.worker, b: a.battle_id, a: a.address }) : link(GAME, "battle", { a: need2(a, "address") }),
1687
+ companion: (a) => link(GAME, "companion", { a: need2(a, "address"), m: a.monster_id }),
1688
+ hunt: (a) => link(GAME, "hunt", { a: need2(a, "address") }),
1689
+ market: (a) => link(TRADE, "market", { venue: a.venue ?? "token", m: need2(a, "market") }),
1690
+ vault: (a) => link(TRADE, "vault", { venue: a.venue ?? "token", m: need2(a, "market"), v: a.vault ?? "amm" }),
1691
+ position: (a) => link(TRADE, "position", { venue: a.venue ?? "token", a: need2(a, "address") }),
1692
+ bridge: (a) => link(TRADE, "bridge", { a: a.address, chain: a.chain, tx: a.tx })
1693
+ };
1694
+ function need2(args, key) {
1695
+ if (!args[key]) throw new Error(`${key} is required to show this.`);
1696
+ return args[key];
1697
+ }
1698
+ async function ownAddress() {
1699
+ if (READ_ONLY) return void 0;
1700
+ const { wallets } = await runtime();
1701
+ return wallets?.ao.address;
1702
+ }
1703
+ function registerShow(server) {
1704
+ const csp = { frameDomains: [GAME, TRADE] };
1705
+ registerAppResource(
1706
+ server,
1707
+ "Paralith view",
1708
+ URI,
1709
+ { description: "Live game and exchange visuals." },
1710
+ async () => ({ contents: [{ uri: URI, mimeType: RESOURCE_MIME_TYPE, text: viewHtml(), _meta: { ui: { csp } } }] })
1711
+ );
1712
+ registerAppTool(server, "show", {
1713
+ description: "Show something with the game's and exchange's own visuals, live: battle (follows a player's arena fights, animated), companion (card), hunt, market (order book and chart), vault, position, bridge (transfers). Returns a link; clients that support MCP Apps also display it inline. Address defaults to yours.",
1714
+ inputSchema: {
1715
+ what: z7.enum(Object.keys(VIEWS)),
1716
+ address: z7.string().optional().describe("AO address; omit for yours."),
1717
+ monster_id: z7.string().optional().describe("companion: which one; omit for the active companion."),
1718
+ battle_id: z7.string().optional().describe("battle: a specific battle, with worker."),
1719
+ worker: z7.string().optional().describe("battle: the battle worker process id."),
1720
+ venue: z7.enum(["game", "token"]).optional().describe("market/vault/position. Default token."),
1721
+ market: z7.string().optional().describe("market/vault: the market, as exchange_markets lists it."),
1722
+ vault: z7.enum(["amm", "active", "grid"]).optional(),
1723
+ chain: z7.enum(["base", "solana", "sui"]).optional().describe("bridge: with tx, one transfer."),
1724
+ tx: z7.string().optional()
1725
+ },
1726
+ annotations: { readOnlyHint: true, openWorldHint: true },
1727
+ _meta: { ui: { resourceUri: URI } }
1728
+ }, async (args) => {
1729
+ try {
1730
+ const view = VIEWS[args.what]({ ...args, address: args.address ?? await ownAddress() });
1731
+ return { structuredContent: { what: args.what, view }, content: [{ type: "text", text: `${args.what}: ${view}` }] };
1732
+ } catch (error) {
1733
+ return { isError: true, content: [{ type: "text", text: error.message }] };
1734
+ }
1735
+ });
1736
+ }
1737
+
1738
+ // src/progress.mjs
1739
+ import { z as z8 } from "zod";
1740
+ var VENUES = { game: graph.processes.internalVenue, token: graph.processes.externalVenue };
1741
+ var count = (x) => Array.isArray(x) ? x.length : Object.values(x ?? {}).reduce((n, v) => n + Number(v || 0), 0);
1742
+ async function targets(who) {
1743
+ const { wallets } = await runtime();
1744
+ const agents = readAgents();
1745
+ const self = wallets && { name: process.env.PARALITH_AGENT ?? "you", addresses: publicAddresses(wallets) };
1746
+ if (!who || who === "me") {
1747
+ if (!self) throw new Error("This server has no wallet; pass who: an AO address.");
1748
+ return [self];
1749
+ }
1750
+ if (who === "all") {
1751
+ const list = Object.entries(agents).map(([name, a]) => ({ name, addresses: a.addresses }));
1752
+ if (self && !list.some((t) => t.name === self.name)) list.unshift(self);
1753
+ if (!list.length) throw new Error("No agents are set up here. Run: npx -y paralith-mcp@latest setup");
1754
+ return list;
1755
+ }
1756
+ if (agents[who]) return [{ name: who, addresses: agents[who].addresses }];
1757
+ if (ID.test(who)) return [{ name: who, addresses: { ao: who } }];
1758
+ throw new Error(`"${who}" is not an agent here (${Object.keys(agents).join(", ") || "none"}) or an AO address.`);
1759
+ }
1760
+ async function report({ name, addresses: a }) {
1761
+ const { api } = await runtime();
1762
+ const out = { agent: name, addresses: a };
1763
+ const todo = [];
1764
+ const gas = Object.fromEntries(await Promise.all(["base", "solana", "sui"].filter((c) => a[c]).map(async (c) => [c, await gasOf(c, a[c]).catch(() => "unreadable")])));
1765
+ if (Object.keys(gas).length) out.gas = gas;
1766
+ if (!a.ao) {
1767
+ out.note = "AO key not derived yet: it appears once this agent's app has started the server.";
1768
+ return out;
1769
+ }
1770
+ const [p, tokens, venues, redemptions] = await Promise.all([
1771
+ api.readPlayer(a.ao).catch(() => null),
1772
+ Promise.all(Object.entries({ rune: graph.processes.rune, pusd: graph.processes.pusd, ...graph.processes.elements }).map(async ([t, id]) => [t, await readAtoms(id, `balance-${a.ao}`).catch(() => 0n)])),
1773
+ Promise.all(Object.entries(VENUES).map(async ([v, pid]) => [
1774
+ v,
1775
+ await api.readVenuePosition(pid, a.ao).catch(() => null),
1776
+ await api.readVenueVaultPositions(pid, a.ao).catch(() => null)
1777
+ ])),
1778
+ readKey("pusd", "withdrawals").catch(() => null)
1779
+ ]);
1780
+ if (!p) {
1781
+ out.game = { joined: false };
1782
+ todo.push("Join the game (game_join).");
1783
+ } else {
1784
+ const raw = p.monsters?.[p.activeId] ?? p.collection?.[p.activeId];
1785
+ const c = companion(raw);
1786
+ out.game = {
1787
+ faction: p.faction,
1788
+ gold: p.gold,
1789
+ rune: p.inventory?.rune ?? 0,
1790
+ companion: c && { name: c.name, level: c.level, exp: c.exp, energy: c.energy, happiness: c.happiness },
1791
+ arena: { wins: p.wins ?? 0, losses: p.losses ?? 0, rating: p.rating, sessionBattlesLeft: p.battlesRemaining ?? 0 },
1792
+ ...p.battleFleet ? { inBattle: p.battleFleet.battleId } : {},
1793
+ ...p.hunt ? { onHunt: p.hunt.runId } : {}
1794
+ };
1795
+ if (!p.dailyReadyAt || Number(p.dailyReadyAt) <= Date.now()) todo.push("Daily crate is ready (game_daily).");
1796
+ if (count(p.lootboxes)) todo.push(`${count(p.lootboxes)} loot box(es) to open (game_lootbox).`);
1797
+ if (raw && Number(raw.exp ?? 0) >= Number(raw.nextLevelExp ?? Infinity)) todo.push("Companion can level up (game_level_up).");
1798
+ if (c && (c.energy < 25 || c.happiness < 25)) todo.push("Companion is below the arena's 25 energy/happiness: feed or play.");
1799
+ }
1800
+ const held = Object.fromEntries(tokens.filter(([, v]) => v > 0n).map(([t, v]) => [t, formatAtoms(v)]));
1801
+ if (Object.keys(held).length) out.tokens = held;
1802
+ const trading = {};
1803
+ for (const [v, pos, vaults] of venues) {
1804
+ const row = {};
1805
+ if (pos?.orders?.length) row.openOrders = pos.orders.length;
1806
+ if (pos?.fills?.length) row.recentFills = pos.fills.length;
1807
+ const shares = Object.entries(vaults ?? {}).flatMap(([m, r]) => Object.entries(r).filter(([, x]) => BigInt(x?.shares ?? 0) > 0n).map(([s]) => `${m} ${s}`));
1808
+ if (shares.length) row.vaults = shares;
1809
+ if (Object.keys(row).length) trading[v] = row;
1810
+ }
1811
+ if (Object.keys(trading).length) out.trading = trading;
1812
+ const pending = (redemptions ?? []).filter((w) => w.account === a.ao && w.status !== "released");
1813
+ if (pending.length) out.bridge = { pendingRedemptions: pending.length };
1814
+ if (Object.keys(gas).length && Object.values(gas).every((g) => /^0 /.test(g))) {
1815
+ todo.push("No chain gas yet: fund an address above to bridge.");
1816
+ }
1817
+ if (todo.length) out.todo = todo;
1818
+ return out;
1819
+ }
1820
+ function registerProgress(server) {
1821
+ tool(
1822
+ server,
1823
+ "progress",
1824
+ 'How an agent is doing, in one free read: game (companion, Gold, Rune, arena record, live battle or hunt), tokens, open orders and vault shares on both venues, pending bridge redemptions, chain gas, and what needs doing next. who: omit for yourself, "all" for every agent set up on this machine, an agent name (claude, codex, grok) or an AO address.',
1825
+ { who: z8.string().optional() },
1826
+ async ({ who }) => {
1827
+ const list = await Promise.all((await targets(who)).map(report));
1828
+ return list.length === 1 ? list[0] : list;
1829
+ },
1830
+ READ
1831
+ );
1832
+ }
1833
+
1629
1834
  // src/build.mjs
1630
1835
  var DOCS = process.env.PARALITH_DOCS_URL || "https://docs.paralithmarkets.com";
1631
1836
  var INSTRUCTIONS = `Paralith on AO (HyperBEAM): Rune Realm (a companion game), two order books, and the PUSD bridge.
@@ -1642,13 +1847,17 @@ Vaults (pooled liquidity, one amm/active/grid per market): vault_list, vault_pos
1642
1847
  Rune: rune_move withdraw (game -> token) / deposit (token -> game). Bridge: bridge_balances, bridge_faucet, bridge_deposit, bridge_redeem.
1643
1848
  DAO (stake TEST-RUNE; a passed proposal may send one attached message): dao_info, dao_proposals, dao_stake stake/unstake/withdraw, dao_propose, dao_vote.
1644
1849
 
1850
+ To report how you (or every agent set up on this machine) are doing, call progress (who: "all" for every agent); it also lists what needs doing next.
1851
+
1852
+ To let the user SEE something (an arena fight as it happens, a companion, a hunt, a market's book and chart, a vault, a position, bridge transfers), call show: it returns a live link and displays inline where the client supports it.
1853
+
1645
1854
  Rules: if a tool says a write WAS accepted but unconfirmed, never resend it; re-read state instead.
1646
1855
  A refusal ({"error": ...}) costs nothing. Use game_catalog for exact costs and move stats; docs at ${DOCS}/llms-full.txt.`;
1647
1856
  var READ_ONLY_NOTE = `This endpoint is READ-ONLY: it explains the protocol and reads live state for any address.
1648
1857
  To play, trade or bridge, run the local server with a wallet phrase: ${DOCS}/ai/mcp`;
1649
1858
  function buildServer() {
1650
1859
  const server = new McpServer(
1651
- { name: "paralith", version: "0.1.0" },
1860
+ { name: "paralith", version: "0.3.0" },
1652
1861
  { instructions: READ_ONLY ? `${READ_ONLY_NOTE}
1653
1862
 
1654
1863
  ${INSTRUCTIONS}` : INSTRUCTIONS }
@@ -1679,7 +1888,7 @@ ${INSTRUCTIONS}` : INSTRUCTIONS }
1679
1888
  server,
1680
1889
  "read_state",
1681
1890
  `Read any published key of any process, free: /<process>~process@1.0/now/<key>. process is an id or an alias (${Object.keys(PROCESS_ALIASES).join(", ")}). Returns null when absent.`,
1682
- { process: z7.string(), key: z7.string() },
1891
+ { process: z9.string(), key: z9.string() },
1683
1892
  async ({ process: process2, key }) => readKey(process2, key),
1684
1893
  READ
1685
1894
  );
@@ -1688,10 +1897,10 @@ ${INSTRUCTIONS}` : INSTRUCTIONS }
1688
1897
  "send_message",
1689
1898
  "Escape hatch: sign and send a raw message to any process and return its reply. Prefer the specific tools; use this only for an action they do not cover. Tag values must be strings; never use a tag named Target.",
1690
1899
  {
1691
- process: z7.string(),
1692
- tags: z7.record(z7.string(), z7.string()).describe('e.g. {"Action": "Pass.Info"}'),
1693
- data: z7.string().optional(),
1694
- push_outbox: z7.boolean().optional().describe("Deliver the messages this one emits to other processes (e.g. a Transfer to a process).")
1900
+ process: z9.string(),
1901
+ tags: z9.record(z9.string(), z9.string()).describe('e.g. {"Action": "Pass.Info"}'),
1902
+ data: z9.string().optional(),
1903
+ push_outbox: z9.boolean().optional().describe("Deliver the messages this one emits to other processes (e.g. a Transfer to a process).")
1695
1904
  },
1696
1905
  async ({ process: process2, tags: tags2, data, push_outbox }) => {
1697
1906
  if (Object.keys(tags2).some((k) => k.toLowerCase() === "target")) throw new Error("Target is not a usable tag name.");
@@ -1720,6 +1929,8 @@ ${INSTRUCTIONS}` : INSTRUCTIONS }
1720
1929
  registerBridge(server);
1721
1930
  registerDao(server);
1722
1931
  registerVaults(server);
1932
+ registerShow(server);
1933
+ registerProgress(server);
1723
1934
  return server;
1724
1935
  }
1725
1936