paralith-mcp 0.2.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 +10 -2
- package/dist/chains.json +79 -42
- package/dist/client.mjs +1 -1
- package/dist/graph.json +2 -2
- package/dist/http.mjs +160 -25
- package/dist/server.mjs +590 -144
- package/package.json +1 -1
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,
|
|
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": "
|
|
8
|
-
"deployBlock":
|
|
7
|
+
"bridge": "0x871ae9cd37B76F9216076d775928C733687596e6",
|
|
8
|
+
"deployBlock": 52429067,
|
|
9
9
|
"tokens": [
|
|
10
10
|
{
|
|
11
|
-
"address": "
|
|
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": "
|
|
22
|
+
"programId": "41aBnUWVghxSWboMKY2F1MhtAYhPMU83igKbfvtjVjcr",
|
|
23
23
|
"deployed": true,
|
|
24
24
|
"tokens": [
|
|
25
25
|
{
|
|
26
|
-
"mint": "
|
|
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":
|
|
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": "
|
|
41
|
-
"originalPackage": "
|
|
42
|
-
"bridge": "
|
|
43
|
-
"faucet": "
|
|
40
|
+
"package": "0x647f58c931f3f9c48ada70a3f91bc554f8945bcad16967e02a0bd49fffe9aed6",
|
|
41
|
+
"originalPackage": "0x647f58c931f3f9c48ada70a3f91bc554f8945bcad16967e02a0bd49fffe9aed6",
|
|
42
|
+
"bridge": "0x270b73af9e6543d196ba7a3bc160f87aabdc662e8601af46ceb0d465ae103337",
|
|
43
|
+
"faucet": "0x4c549789110006fadc343790b770fffa314269bcbeafd4f48853c3bf3d9639ef",
|
|
44
44
|
"tokens": [
|
|
45
45
|
{
|
|
46
|
-
"type": "
|
|
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": "
|
|
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": "
|
|
65
|
-
"version": "sha256:
|
|
66
|
-
"localHash": "sha256:
|
|
67
|
-
"deployedAt": "2026-10-
|
|
68
|
-
"tx": "
|
|
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": "
|
|
73
|
-
"version": "sha256:
|
|
74
|
-
"localHash": "sha256:
|
|
75
|
-
"deployedAt": "2026-10-
|
|
76
|
-
"tx": "
|
|
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": "
|
|
81
|
-
"version": "
|
|
82
|
-
"localHash": "
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
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": "
|
|
89
|
-
"version": "sha256:
|
|
90
|
-
"localHash": "sha256:
|
|
91
|
-
"deployedAt": "2026-10-
|
|
92
|
-
"tx": "
|
|
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-
|
|
97
|
-
"startedAt": "2026-10-
|
|
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": "
|
|
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: "
|
|
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
|
|
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-
|
|
4
|
+
"deployedAt": "2026-10-10T15:51:09.959Z",
|
|
5
5
|
"processes": {
|
|
6
6
|
"game": "bqkZT1qY8Qpea7QsKZEk8q7m0yUE8-G3Miq8EFXnRJg",
|
|
7
7
|
"rune": "0j4rkkEqUO25aXI8LoDuou2Ibr0GSMGsXrFRxwrSfY8",
|
|
8
|
-
"pusd": "
|
|
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
|
|
9
|
-
import
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
193
|
-
var DIST =
|
|
194
|
-
var graph = JSON.parse(
|
|
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(
|
|
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
|
-
|
|
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
|
|
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);
|
|
@@ -1627,18 +1663,18 @@ function registerVaults(server) {
|
|
|
1627
1663
|
}
|
|
1628
1664
|
|
|
1629
1665
|
// src/show.mjs
|
|
1630
|
-
import
|
|
1631
|
-
import
|
|
1666
|
+
import fs4 from "node:fs";
|
|
1667
|
+
import path4 from "node:path";
|
|
1632
1668
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1633
1669
|
import { z as z7 } from "zod";
|
|
1634
1670
|
import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
|
|
1635
1671
|
var GAME = (process.env.PARALITH_GAME_URL || "https://game.paralithmarkets.com").replace(/\/$/, "");
|
|
1636
1672
|
var TRADE = (process.env.PARALITH_TRADE_URL || "https://trade.paralithmarkets.com").replace(/\/$/, "");
|
|
1637
1673
|
var URI = "ui://paralith/view";
|
|
1638
|
-
var HERE2 =
|
|
1674
|
+
var HERE2 = path4.dirname(fileURLToPath2(import.meta.url));
|
|
1639
1675
|
function viewHtml() {
|
|
1640
|
-
for (const file of [
|
|
1641
|
-
if (
|
|
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");
|
|
1642
1678
|
}
|
|
1643
1679
|
throw new Error("view.html is missing: run npm run build");
|
|
1644
1680
|
}
|
|
@@ -1699,6 +1735,102 @@ function registerShow(server) {
|
|
|
1699
1735
|
});
|
|
1700
1736
|
}
|
|
1701
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
|
+
|
|
1702
1834
|
// src/build.mjs
|
|
1703
1835
|
var DOCS = process.env.PARALITH_DOCS_URL || "https://docs.paralithmarkets.com";
|
|
1704
1836
|
var INSTRUCTIONS = `Paralith on AO (HyperBEAM): Rune Realm (a companion game), two order books, and the PUSD bridge.
|
|
@@ -1715,6 +1847,8 @@ Vaults (pooled liquidity, one amm/active/grid per market): vault_list, vault_pos
|
|
|
1715
1847
|
Rune: rune_move withdraw (game -> token) / deposit (token -> game). Bridge: bridge_balances, bridge_faucet, bridge_deposit, bridge_redeem.
|
|
1716
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.
|
|
1717
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
|
+
|
|
1718
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.
|
|
1719
1853
|
|
|
1720
1854
|
Rules: if a tool says a write WAS accepted but unconfirmed, never resend it; re-read state instead.
|
|
@@ -1723,7 +1857,7 @@ var READ_ONLY_NOTE = `This endpoint is READ-ONLY: it explains the protocol and r
|
|
|
1723
1857
|
To play, trade or bridge, run the local server with a wallet phrase: ${DOCS}/ai/mcp`;
|
|
1724
1858
|
function buildServer() {
|
|
1725
1859
|
const server = new McpServer(
|
|
1726
|
-
{ name: "paralith", version: "0.
|
|
1860
|
+
{ name: "paralith", version: "0.3.0" },
|
|
1727
1861
|
{ instructions: READ_ONLY ? `${READ_ONLY_NOTE}
|
|
1728
1862
|
|
|
1729
1863
|
${INSTRUCTIONS}` : INSTRUCTIONS }
|
|
@@ -1754,7 +1888,7 @@ ${INSTRUCTIONS}` : INSTRUCTIONS }
|
|
|
1754
1888
|
server,
|
|
1755
1889
|
"read_state",
|
|
1756
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.`,
|
|
1757
|
-
{ process:
|
|
1891
|
+
{ process: z9.string(), key: z9.string() },
|
|
1758
1892
|
async ({ process: process2, key }) => readKey(process2, key),
|
|
1759
1893
|
READ
|
|
1760
1894
|
);
|
|
@@ -1763,10 +1897,10 @@ ${INSTRUCTIONS}` : INSTRUCTIONS }
|
|
|
1763
1897
|
"send_message",
|
|
1764
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.",
|
|
1765
1899
|
{
|
|
1766
|
-
process:
|
|
1767
|
-
tags:
|
|
1768
|
-
data:
|
|
1769
|
-
push_outbox:
|
|
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).")
|
|
1770
1904
|
},
|
|
1771
1905
|
async ({ process: process2, tags: tags2, data, push_outbox }) => {
|
|
1772
1906
|
if (Object.keys(tags2).some((k) => k.toLowerCase() === "target")) throw new Error("Target is not a usable tag name.");
|
|
@@ -1796,6 +1930,7 @@ ${INSTRUCTIONS}` : INSTRUCTIONS }
|
|
|
1796
1930
|
registerDao(server);
|
|
1797
1931
|
registerVaults(server);
|
|
1798
1932
|
registerShow(server);
|
|
1933
|
+
registerProgress(server);
|
|
1799
1934
|
return server;
|
|
1800
1935
|
}
|
|
1801
1936
|
|