dero-mcp-server 0.1.1 → 0.2.2
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 +79 -6
- package/data/docs-index.json +5702 -0
- package/dist/citations.d.ts +70 -0
- package/dist/citations.d.ts.map +1 -0
- package/dist/citations.js +162 -0
- package/dist/citations.js.map +1 -0
- package/dist/composites/_shared.d.ts +119 -0
- package/dist/composites/_shared.d.ts.map +1 -0
- package/dist/composites/_shared.js +152 -0
- package/dist/composites/_shared.js.map +1 -0
- package/dist/composites/diagnose-chain-health.d.ts +64 -0
- package/dist/composites/diagnose-chain-health.d.ts.map +1 -0
- package/dist/composites/diagnose-chain-health.js +144 -0
- package/dist/composites/diagnose-chain-health.js.map +1 -0
- package/dist/composites/estimate-deploy-cost.d.ts +83 -0
- package/dist/composites/estimate-deploy-cost.d.ts.map +1 -0
- package/dist/composites/estimate-deploy-cost.js +116 -0
- package/dist/composites/estimate-deploy-cost.js.map +1 -0
- package/dist/composites/explain-smart-contract.d.ts +64 -0
- package/dist/composites/explain-smart-contract.d.ts.map +1 -0
- package/dist/composites/explain-smart-contract.js +149 -0
- package/dist/composites/explain-smart-contract.js.map +1 -0
- package/dist/composites/recommend-docs-path.d.ts +97 -0
- package/dist/composites/recommend-docs-path.d.ts.map +1 -0
- package/dist/composites/recommend-docs-path.js +149 -0
- package/dist/composites/recommend-docs-path.js.map +1 -0
- package/dist/composites/trace-transaction-with-context.d.ts +107 -0
- package/dist/composites/trace-transaction-with-context.d.ts.map +1 -0
- package/dist/composites/trace-transaction-with-context.js +217 -0
- package/dist/composites/trace-transaction-with-context.js.map +1 -0
- package/dist/docs-parse.d.ts +30 -0
- package/dist/docs-parse.d.ts.map +1 -0
- package/dist/docs-parse.js +147 -0
- package/dist/docs-parse.js.map +1 -0
- package/dist/docs.d.ts +101 -0
- package/dist/docs.d.ts.map +1 -0
- package/dist/docs.js +172 -0
- package/dist/docs.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +417 -100
- package/dist/server.js.map +1 -1
- package/dist/tool-descriptions.d.ts +50 -0
- package/dist/tool-descriptions.d.ts.map +1 -0
- package/dist/tool-descriptions.js +246 -0
- package/dist/tool-descriptions.js.map +1 -0
- package/package.json +15 -3
- package/.github/workflows/ci.yml +0 -62
- package/docs/example-agent-flows.md +0 -236
- package/docs/mcp-agent-ready-evidence.md +0 -108
- package/glama.json +0 -6
- package/scripts/doctor.sh +0 -85
- package/scripts/flow-test.ts +0 -257
- package/scripts/mcp-smoke-probes.ts +0 -168
- package/server.json +0 -23
- package/src/index.ts +0 -30
- package/src/rpc.ts +0 -60
- package/src/server.ts +0 -636
- package/tsconfig.json +0 -16
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
# Example Agent Flows
|
|
2
|
-
|
|
3
|
-
How to use DERO MCP tools in multi-step conversations. These examples work in Cursor, Claude Desktop, or any MCP client with `dero-mcp-server` configured.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Flow 1: Network Health Check
|
|
8
|
-
|
|
9
|
-
**User prompt:**
|
|
10
|
-
> "Is my DERO node synced?"
|
|
11
|
-
|
|
12
|
-
**Agent steps:**
|
|
13
|
-
|
|
14
|
-
1. `dero_daemon_ping` — confirm daemon is reachable
|
|
15
|
-
2. `dero_get_info` — get version, network, topoheight, difficulty
|
|
16
|
-
3. `dero_get_height` — get height, stableheight, topoheight
|
|
17
|
-
|
|
18
|
-
**What to look for:**
|
|
19
|
-
- Ping returns `"Pong"` → daemon is up
|
|
20
|
-
- `topoheight` is increasing over time → node is syncing
|
|
21
|
-
- `stableheight` close to `topoheight` → node is caught up
|
|
22
|
-
- Compare `topoheight` to a block explorer to verify sync status
|
|
23
|
-
|
|
24
|
-
**Example response:**
|
|
25
|
-
> Your node is reachable and synced. Current topoheight: 4,521,307. Network: mainnet. Daemon version: 3.5.1-1.DERO.STARGATE+05.
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Flow 2: Smart Contract Inspection
|
|
30
|
-
|
|
31
|
-
**User prompt:**
|
|
32
|
-
> "Show me the stored variables for SCID abc123..."
|
|
33
|
-
|
|
34
|
-
**Agent steps:**
|
|
35
|
-
|
|
36
|
-
1. `dero_get_sc` with `scid`, `variables: true`, `code: false`
|
|
37
|
-
2. Parse the `stringkeys` and `balances` from the response
|
|
38
|
-
3. Interpret Uint64/String values using DVM-BASIC conventions
|
|
39
|
-
|
|
40
|
-
**What to look for:**
|
|
41
|
-
- `stringkeys` contains stored state (key-value pairs)
|
|
42
|
-
- Keys starting with lowercase are typically private state
|
|
43
|
-
- `balances` shows token holdings if the contract manages assets
|
|
44
|
-
- `code` field contains DVM-BASIC source if `code: true`
|
|
45
|
-
|
|
46
|
-
**Example prompt:**
|
|
47
|
-
> "What's stored in the name registry contract?"
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
dero_get_sc({
|
|
51
|
-
scid: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
52
|
-
variables: true,
|
|
53
|
-
code: false
|
|
54
|
-
})
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## Flow 3: Transaction Lookup
|
|
60
|
-
|
|
61
|
-
**User prompt:**
|
|
62
|
-
> "What happened in transaction 7f3a...?"
|
|
63
|
-
|
|
64
|
-
**Agent steps:**
|
|
65
|
-
|
|
66
|
-
1. `dero_get_transaction` with `txs_hashes: ["7f3a..."]`, `decode_as_json: 1`
|
|
67
|
-
2. Summarize transaction type, transfers, and SC invocations
|
|
68
|
-
3. Link to block height from response metadata
|
|
69
|
-
|
|
70
|
-
**What to look for:**
|
|
71
|
-
- `txs` array contains transaction details
|
|
72
|
-
- `sc_args` shows smart contract function calls
|
|
73
|
-
- `as_hex` is the raw transaction (skip unless debugging)
|
|
74
|
-
- `block_height` and `in_pool` indicate confirmation status
|
|
75
|
-
|
|
76
|
-
**Example:**
|
|
77
|
-
> Transaction 7f3a... was confirmed at block 4,521,200. It invoked the "Register" function on the name registry with argument "myname".
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
## Flow 4: Name Resolution
|
|
82
|
-
|
|
83
|
-
**User prompt:**
|
|
84
|
-
> "What address owns the name 'dero'?"
|
|
85
|
-
|
|
86
|
-
**Agent steps:**
|
|
87
|
-
|
|
88
|
-
1. `dero_name_to_address` with `name: "dero"`, `topoheight: -1`
|
|
89
|
-
2. Return the resolved address
|
|
90
|
-
|
|
91
|
-
**What to look for:**
|
|
92
|
-
- `address` field contains the DERO address (starts with `dero1` or `deto1`)
|
|
93
|
-
- If name is not registered, the RPC returns an error
|
|
94
|
-
- `topoheight: -1` means "latest chain state"
|
|
95
|
-
|
|
96
|
-
**Example:**
|
|
97
|
-
> The name "dero" resolves to `dero1qy...` (truncated). This is public on-chain data visible to anyone.
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## Flow 5: Block Exploration
|
|
102
|
-
|
|
103
|
-
**User prompt:**
|
|
104
|
-
> "Show me block 4,500,000"
|
|
105
|
-
|
|
106
|
-
**Agent steps:**
|
|
107
|
-
|
|
108
|
-
1. `dero_get_block` with `height: 4500000`
|
|
109
|
-
2. Summarize: timestamp, miner reward, transaction count, hash
|
|
110
|
-
|
|
111
|
-
**Alternative — by hash:**
|
|
112
|
-
1. `dero_get_block_header_by_hash` with `hash: "abc123..."`
|
|
113
|
-
|
|
114
|
-
**What to look for:**
|
|
115
|
-
- `block_header.timestamp` — Unix timestamp of block
|
|
116
|
-
- `block_header.reward` — miner reward in atomic units (divide by 100000 for DERO)
|
|
117
|
-
- `txs` — list of transaction hashes in the block
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## Flow 6: Contract Deploy Prep (Read-Only)
|
|
122
|
-
|
|
123
|
-
**User prompt:**
|
|
124
|
-
> "Estimate gas for deploying this contract"
|
|
125
|
-
|
|
126
|
-
**Agent steps:**
|
|
127
|
-
|
|
128
|
-
1. Validate DVM-BASIC syntax (use `dvm-basic` Cursor skill if available)
|
|
129
|
-
2. `dero_get_gas_estimate` with `sc: "<contract source>"`
|
|
130
|
-
3. Report compute and storage costs
|
|
131
|
-
|
|
132
|
-
**Important:** This MCP server is **read-only**. It cannot deploy contracts. After estimating gas, tell the user to deploy via:
|
|
133
|
-
- DERO CLI wallet (`curl` to wallet RPC)
|
|
134
|
-
- Engram wallet GUI
|
|
135
|
-
- XSWD browser integration
|
|
136
|
-
|
|
137
|
-
**Example:**
|
|
138
|
-
> Gas estimate: 5,000 compute + 12,000 storage = 17,000 total. Deploy via your wallet — this MCP cannot submit transactions.
|
|
139
|
-
|
|
140
|
-
---
|
|
141
|
-
|
|
142
|
-
## Flow 7: Mempool Check
|
|
143
|
-
|
|
144
|
-
**User prompt:**
|
|
145
|
-
> "Are there pending transactions?"
|
|
146
|
-
|
|
147
|
-
**Agent steps:**
|
|
148
|
-
|
|
149
|
-
1. `dero_get_tx_pool`
|
|
150
|
-
2. Report count of pending transaction hashes
|
|
151
|
-
|
|
152
|
-
**What to look for:**
|
|
153
|
-
- `tx_hashes` array — pending transactions waiting for blocks
|
|
154
|
-
- Empty array means mempool is clear
|
|
155
|
-
- Large mempool may indicate network congestion
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
## Flow 8: Encrypted Balance Lookup
|
|
160
|
-
|
|
161
|
-
**User prompt:**
|
|
162
|
-
> "Get the encrypted balance blob for address dero1qy..."
|
|
163
|
-
|
|
164
|
-
**Agent steps:**
|
|
165
|
-
|
|
166
|
-
1. `dero_get_encrypted_balance` with `address`, `topoheight: -1`
|
|
167
|
-
2. Return the encrypted balance data
|
|
168
|
-
|
|
169
|
-
**Important:** This returns **encrypted** balance data, not cleartext. Only the address owner can decrypt it with their private keys. This is useful for:
|
|
170
|
-
- Building transactions (ring signatures need balance commitments)
|
|
171
|
-
- Verifying an address exists on-chain
|
|
172
|
-
|
|
173
|
-
---
|
|
174
|
-
|
|
175
|
-
## Flow 9: Mining Template (Advanced)
|
|
176
|
-
|
|
177
|
-
**User prompt:**
|
|
178
|
-
> "Get a block template for mining"
|
|
179
|
-
|
|
180
|
-
**Agent steps:**
|
|
181
|
-
|
|
182
|
-
1. `dero_get_block_template` with `wallet_address: "dero1qy..."`
|
|
183
|
-
2. Return the template blob for mining software
|
|
184
|
-
|
|
185
|
-
**Note:** This is for miners integrating with pools or solo mining. Most users won't need this.
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## Combining Flows
|
|
190
|
-
|
|
191
|
-
Real conversations often combine multiple flows:
|
|
192
|
-
|
|
193
|
-
**User:** "Check if my node is synced, then show me the latest block and any pending transactions."
|
|
194
|
-
|
|
195
|
-
**Agent:**
|
|
196
|
-
1. `dero_daemon_ping` → confirm reachable
|
|
197
|
-
2. `dero_get_info` → get topoheight
|
|
198
|
-
3. `dero_get_last_block_header` → latest block details
|
|
199
|
-
4. `dero_get_tx_pool` → pending transactions
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
## Tips for Agents
|
|
204
|
-
|
|
205
|
-
1. **Always ping first** if unsure about daemon connectivity
|
|
206
|
-
2. **Use `topoheight: -1`** for latest state in queries that accept it
|
|
207
|
-
3. **SCIDs are 64-character hex** — validate format before calling
|
|
208
|
-
4. **Atomic units**: DERO amounts are in 1/100000 units (5 decimals)
|
|
209
|
-
5. **Read-only boundary**: This MCP cannot send transactions, deploy contracts, or modify state. Guide users to wallet tools for writes.
|
|
210
|
-
6. **Use structured errors**: failed tools return `_meta.error` with `code`, `hint`, and `retryable` for recovery logic.
|
|
211
|
-
|
|
212
|
-
---
|
|
213
|
-
|
|
214
|
-
## Related Skills
|
|
215
|
-
|
|
216
|
-
If using Cursor, these skills complement the MCP tools:
|
|
217
|
-
|
|
218
|
-
| Skill | Use for |
|
|
219
|
-
|---|---|
|
|
220
|
-
| `dero-rpc` | Direct curl commands, wallet RPC (transfers, scinvoke) |
|
|
221
|
-
| `dvm-basic` | Writing and debugging smart contract code |
|
|
222
|
-
| `smart-contracts` | Deployment workflows, ownership transfer |
|
|
223
|
-
| `tela-publisher` | Deploying TELA web apps on-chain |
|
|
224
|
-
| `gnomon-indexer` | Discovering contracts by stored variables |
|
|
225
|
-
|
|
226
|
-
The MCP provides **live chain reads**. Skills provide **workflow guidance** and access to wallet operations the MCP intentionally excludes.
|
|
227
|
-
|
|
228
|
-
## Built-in MCP Prompts
|
|
229
|
-
|
|
230
|
-
These prompts are available from `prompts/list`:
|
|
231
|
-
|
|
232
|
-
- `network_health_check`
|
|
233
|
-
- `inspect_smart_contract`
|
|
234
|
-
- `trace_transaction`
|
|
235
|
-
|
|
236
|
-
Use `prompts/get` with arguments, then execute the suggested tool sequence.
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
# DERO MCP Agent-Ready Evidence
|
|
2
|
-
|
|
3
|
-
**Last updated:** 2026-05-19
|
|
4
|
-
**Repo:** `DHEBP/dero-mcp-server` @ `6630dee`
|
|
5
|
-
**Mode:** Local stdio MCP (read-only daemon access)
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## One-line verdict
|
|
10
|
-
|
|
11
|
-
DERO MCP is **agent-ready for local stdio usage**: surface contract is stable, diagnostics pass, flow tests pass, and CI now includes MCP smoke probes.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Verified surface
|
|
16
|
-
|
|
17
|
-
| Primitive | Count | Notes |
|
|
18
|
-
|---|---:|---|
|
|
19
|
-
| Tools | 17 | Read-only daemon methods only |
|
|
20
|
-
| Resources | 3 | Server info, safety boundary, example flows |
|
|
21
|
-
| Prompts | 3 | Network health, SC inspection, tx tracing |
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Proof commands and outcomes
|
|
26
|
-
|
|
27
|
-
### 1) MCP smoke probes
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
npm run smoke:mcp
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Result (latest run):
|
|
34
|
-
|
|
35
|
-
- `tools/list` parity: **17**
|
|
36
|
-
- `resources/list` parity: **3**
|
|
37
|
-
- `prompts/list` parity: **3**
|
|
38
|
-
- `prompts/get` check: **pass**
|
|
39
|
-
- Structured error probe (`_meta.error`): **pass**
|
|
40
|
-
|
|
41
|
-
### 2) Flow tests
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npm run test:flows
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Result (latest run):
|
|
48
|
-
|
|
49
|
-
- **10 passed**
|
|
50
|
-
- **0 failed**
|
|
51
|
-
- **0 skipped**
|
|
52
|
-
|
|
53
|
-
### 3) Daemon connectivity doctor
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
npm run doctor
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Result (latest run):
|
|
60
|
-
|
|
61
|
-
- TCP reachability: **pass**
|
|
62
|
-
- `DERO.Ping`: **pass**
|
|
63
|
-
- `DERO.GetInfo`: **pass**
|
|
64
|
-
|
|
65
|
-
### 4) Build and type safety
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
npm run build
|
|
69
|
-
npm run typecheck
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Result (latest run):
|
|
73
|
-
|
|
74
|
-
- Build: **pass**
|
|
75
|
-
- Typecheck: **pass**
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## Security boundary (explicit)
|
|
80
|
-
|
|
81
|
-
This MCP server remains **read-only** by design.
|
|
82
|
-
|
|
83
|
-
Excluded methods include:
|
|
84
|
-
|
|
85
|
-
- Wallet mutation calls (e.g., `transfer`, `scinvoke`)
|
|
86
|
-
- `DERO.SendRawTransaction`
|
|
87
|
-
- `DERO.SubmitBlock`
|
|
88
|
-
|
|
89
|
-
Write operations must remain outside this server unless an explicit wallet-write gate policy is introduced.
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
## CI gate
|
|
94
|
-
|
|
95
|
-
Current CI runs:
|
|
96
|
-
|
|
97
|
-
1. `npm run build`
|
|
98
|
-
2. `npm run smoke:mcp`
|
|
99
|
-
3. `npm run test:flows`
|
|
100
|
-
4. `tsc --noEmit`
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
## Deferred items
|
|
105
|
-
|
|
106
|
-
- Wallet-write support (intentionally deferred)
|
|
107
|
-
- Streamable HTTP/SSE transport (not required for current stdio-first strategy)
|
|
108
|
-
- Domain DNS discovery artifacts (`.well-known`, `_mcp`, `_agentroot`, `_llms`) until remote transport exists
|
package/glama.json
DELETED
package/scripts/doctor.sh
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# DERO MCP Server — connectivity doctor
|
|
3
|
-
# Checks daemon reachability before using the MCP server.
|
|
4
|
-
#
|
|
5
|
-
# Usage:
|
|
6
|
-
# ./scripts/doctor.sh # uses DERO_DAEMON_URL or default
|
|
7
|
-
# ./scripts/doctor.sh http://127.0.0.1:10102 # explicit URL
|
|
8
|
-
# DERO_DAEMON_URL=http://... ./scripts/doctor.sh
|
|
9
|
-
|
|
10
|
-
set -euo pipefail
|
|
11
|
-
|
|
12
|
-
# Default: public RPC (same as server default)
|
|
13
|
-
DEFAULT_URL="http://82.65.143.182:10102"
|
|
14
|
-
|
|
15
|
-
# Accept CLI arg or env var
|
|
16
|
-
DAEMON_URL="${1:-${DERO_DAEMON_URL:-$DEFAULT_URL}}"
|
|
17
|
-
DAEMON_URL="${DAEMON_URL%/}" # strip trailing slash
|
|
18
|
-
|
|
19
|
-
echo "DERO MCP Doctor"
|
|
20
|
-
echo "==============="
|
|
21
|
-
echo "Daemon URL: ${DAEMON_URL}"
|
|
22
|
-
echo ""
|
|
23
|
-
|
|
24
|
-
fail=0
|
|
25
|
-
|
|
26
|
-
# Check 1: Basic TCP reachability (extract host:port)
|
|
27
|
-
host_port=$(echo "$DAEMON_URL" | sed -E 's|https?://||' | sed 's|/.*||')
|
|
28
|
-
host=$(echo "$host_port" | cut -d: -f1)
|
|
29
|
-
port=$(echo "$host_port" | cut -d: -f2)
|
|
30
|
-
if [[ -z "$port" || "$port" == "$host" ]]; then
|
|
31
|
-
port=10102
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
echo "1. TCP connectivity to ${host}:${port}"
|
|
35
|
-
if nc -z -w 5 "$host" "$port" 2>/dev/null; then
|
|
36
|
-
echo " OK — port reachable"
|
|
37
|
-
else
|
|
38
|
-
echo " FAIL — cannot reach ${host}:${port}"
|
|
39
|
-
echo " Hint: Is the daemon running? Is the port open?"
|
|
40
|
-
fail=$((fail + 1))
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
echo ""
|
|
44
|
-
|
|
45
|
-
# Check 2: DERO.Ping RPC call
|
|
46
|
-
echo "2. DERO.Ping RPC"
|
|
47
|
-
ping_response=$(curl -sS -m 10 -X POST "${DAEMON_URL}/json_rpc" \
|
|
48
|
-
-H "Content-Type: application/json" \
|
|
49
|
-
-d '{"jsonrpc":"2.0","method":"DERO.Ping","id":1}' 2>/dev/null || echo '{"error":"curl_failed"}')
|
|
50
|
-
|
|
51
|
-
if echo "$ping_response" | grep -Eq '"status":"OK"|"result":"Pong ?"' ; then
|
|
52
|
-
echo " OK — daemon responded to ping"
|
|
53
|
-
else
|
|
54
|
-
echo " FAIL — DERO.Ping did not return a known success shape"
|
|
55
|
-
echo " Response: $ping_response"
|
|
56
|
-
fail=$((fail + 1))
|
|
57
|
-
fi
|
|
58
|
-
|
|
59
|
-
echo ""
|
|
60
|
-
|
|
61
|
-
# Check 3: DERO.GetInfo (confirms chain sync)
|
|
62
|
-
echo "3. DERO.GetInfo RPC"
|
|
63
|
-
info_response=$(curl -sS -m 10 -X POST "${DAEMON_URL}/json_rpc" \
|
|
64
|
-
-H "Content-Type: application/json" \
|
|
65
|
-
-d '{"jsonrpc":"2.0","method":"DERO.GetInfo","id":1}' 2>/dev/null || echo '{"error":"curl_failed"}')
|
|
66
|
-
|
|
67
|
-
if echo "$info_response" | grep -q '"topoheight"'; then
|
|
68
|
-
topoheight=$(echo "$info_response" | grep -oE '"topoheight":[0-9]+' | grep -oE '[0-9]+')
|
|
69
|
-
echo " OK — chain topoheight: ${topoheight:-unknown}"
|
|
70
|
-
else
|
|
71
|
-
echo " FAIL — DERO.GetInfo did not return chain data"
|
|
72
|
-
echo " Response: $info_response"
|
|
73
|
-
fail=$((fail + 1))
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
echo ""
|
|
77
|
-
|
|
78
|
-
# Summary
|
|
79
|
-
if [[ "$fail" -eq 0 ]]; then
|
|
80
|
-
echo "All checks passed. MCP server should work against this daemon."
|
|
81
|
-
exit 0
|
|
82
|
-
else
|
|
83
|
-
echo "${fail} check(s) failed. Fix connectivity before using MCP."
|
|
84
|
-
exit 1
|
|
85
|
-
fi
|
package/scripts/flow-test.ts
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env npx tsx
|
|
2
|
-
/**
|
|
3
|
-
* DERO MCP Server — Flow Test
|
|
4
|
-
*
|
|
5
|
-
* Tests the daemon RPC calls that the MCP tools wrap.
|
|
6
|
-
* Run this against a live daemon (local or remote) to verify connectivity
|
|
7
|
-
* and that the RPC methods return expected data shapes.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* npx tsx scripts/flow-test.ts
|
|
11
|
-
* npx tsx scripts/flow-test.ts http://127.0.0.1:10102
|
|
12
|
-
* DERO_DAEMON_URL=http://... npx tsx scripts/flow-test.ts
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const DEFAULT_URL = "http://82.65.143.182:10102";
|
|
16
|
-
|
|
17
|
-
type FlowStatus = "pass" | "fail" | "skip";
|
|
18
|
-
type FlowResult = {
|
|
19
|
-
id: string;
|
|
20
|
-
name: string;
|
|
21
|
-
status: FlowStatus;
|
|
22
|
-
message?: string;
|
|
23
|
-
durationMs: number;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
async function deroRpc<T = unknown>(
|
|
27
|
-
endpoint: string,
|
|
28
|
-
method: string,
|
|
29
|
-
params?: unknown
|
|
30
|
-
): Promise<T> {
|
|
31
|
-
const res = await fetch(`${endpoint}/json_rpc`, {
|
|
32
|
-
method: "POST",
|
|
33
|
-
headers: { "Content-Type": "application/json" },
|
|
34
|
-
body: JSON.stringify({
|
|
35
|
-
jsonrpc: "2.0",
|
|
36
|
-
id: "1",
|
|
37
|
-
method,
|
|
38
|
-
params,
|
|
39
|
-
}),
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (!res.ok) {
|
|
43
|
-
throw new Error(`HTTP ${res.status} from daemon`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const data = await res.json();
|
|
47
|
-
if (data.error) {
|
|
48
|
-
throw new Error(`RPC error: ${JSON.stringify(data.error)}`);
|
|
49
|
-
}
|
|
50
|
-
return data.result;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function assert(condition: unknown, message: string): void {
|
|
54
|
-
if (!condition) throw new Error(message);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function runFlow(
|
|
58
|
-
id: string,
|
|
59
|
-
name: string,
|
|
60
|
-
fn: () => Promise<void>
|
|
61
|
-
): Promise<FlowResult> {
|
|
62
|
-
const start = performance.now();
|
|
63
|
-
try {
|
|
64
|
-
await fn();
|
|
65
|
-
return { id, name, status: "pass", durationMs: Math.round(performance.now() - start) };
|
|
66
|
-
} catch (error) {
|
|
67
|
-
return {
|
|
68
|
-
id,
|
|
69
|
-
name,
|
|
70
|
-
status: "fail",
|
|
71
|
-
message: error instanceof Error ? error.message : String(error),
|
|
72
|
-
durationMs: Math.round(performance.now() - start),
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async function runAllFlows(daemonUrl: string): Promise<FlowResult[]> {
|
|
78
|
-
const results: FlowResult[] = [];
|
|
79
|
-
|
|
80
|
-
// Flow 1: Ping
|
|
81
|
-
results.push(
|
|
82
|
-
await runFlow("ping", "DERO.Ping — daemon reachable", async () => {
|
|
83
|
-
const result = await deroRpc<string>(daemonUrl, "DERO.Ping");
|
|
84
|
-
assert(result === "Pong " || result === "Pong", `Expected 'Pong', got '${result}'`);
|
|
85
|
-
})
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
// Flow 2: Echo
|
|
89
|
-
results.push(
|
|
90
|
-
await runFlow("echo", "DERO.Echo — roundtrip strings", async () => {
|
|
91
|
-
const words = ["hello", "dero", "mcp"];
|
|
92
|
-
const result = await deroRpc<string>(daemonUrl, "DERO.Echo", words);
|
|
93
|
-
assert(typeof result === "string", "Expected string response");
|
|
94
|
-
assert(result.includes("DERO") || words.some(w => result.includes(w)), "Echo should contain input or DERO");
|
|
95
|
-
})
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
// Flow 3: GetInfo
|
|
99
|
-
let topoheight: number | undefined;
|
|
100
|
-
results.push(
|
|
101
|
-
await runFlow("get-info", "DERO.GetInfo — chain metadata", async () => {
|
|
102
|
-
const result = await deroRpc<{
|
|
103
|
-
topoheight?: number;
|
|
104
|
-
height?: number;
|
|
105
|
-
network?: string;
|
|
106
|
-
version?: string;
|
|
107
|
-
}>(daemonUrl, "DERO.GetInfo");
|
|
108
|
-
assert(typeof result.topoheight === "number", "Missing topoheight");
|
|
109
|
-
assert(typeof result.height === "number", "Missing height");
|
|
110
|
-
topoheight = result.topoheight;
|
|
111
|
-
})
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
// Flow 4: GetHeight
|
|
115
|
-
results.push(
|
|
116
|
-
await runFlow("get-height", "DERO.GetHeight — block heights", async () => {
|
|
117
|
-
const result = await deroRpc<{
|
|
118
|
-
height?: number;
|
|
119
|
-
stableheight?: number;
|
|
120
|
-
topoheight?: number;
|
|
121
|
-
}>(daemonUrl, "DERO.GetHeight");
|
|
122
|
-
assert(typeof result.height === "number", "Missing height");
|
|
123
|
-
assert(typeof result.topoheight === "number", "Missing topoheight");
|
|
124
|
-
})
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
// Flow 5: GetBlockCount
|
|
128
|
-
results.push(
|
|
129
|
-
await runFlow("get-block-count", "DERO.GetBlockCount — total blocks", async () => {
|
|
130
|
-
const result = await deroRpc<{ count?: number }>(daemonUrl, "DERO.GetBlockCount");
|
|
131
|
-
assert(typeof result.count === "number", "Missing count");
|
|
132
|
-
assert(result.count > 0, "Block count should be > 0");
|
|
133
|
-
})
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
// Flow 6: GetLastBlockHeader
|
|
137
|
-
results.push(
|
|
138
|
-
await runFlow("get-last-block-header", "DERO.GetLastBlockHeader — tip block", async () => {
|
|
139
|
-
const result = await deroRpc<{ block_header?: { height?: number; hash?: string } }>(
|
|
140
|
-
daemonUrl,
|
|
141
|
-
"DERO.GetLastBlockHeader"
|
|
142
|
-
);
|
|
143
|
-
assert(result.block_header, "Missing block_header");
|
|
144
|
-
assert(typeof result.block_header.height === "number", "Missing height in header");
|
|
145
|
-
})
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// Flow 7: GetBlock by height (use topoheight - 10 for safety)
|
|
149
|
-
results.push(
|
|
150
|
-
await runFlow("get-block-by-height", "DERO.GetBlock — fetch by height", async () => {
|
|
151
|
-
const testHeight = Math.max(1, (topoheight ?? 100) - 10);
|
|
152
|
-
const result = await deroRpc<{ block_header?: unknown }>(daemonUrl, "DERO.GetBlock", {
|
|
153
|
-
height: testHeight,
|
|
154
|
-
});
|
|
155
|
-
assert(result.block_header, "Missing block_header in response");
|
|
156
|
-
})
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
// Flow 8: GetTxPool (may be empty, just check shape)
|
|
160
|
-
results.push(
|
|
161
|
-
await runFlow("get-tx-pool", "DERO.GetTxPool — mempool check", async () => {
|
|
162
|
-
const result = await deroRpc<{ tx_hashes?: string[] }>(daemonUrl, "DERO.GetTxPool");
|
|
163
|
-
// tx_hashes may be null/undefined if empty, which is fine
|
|
164
|
-
assert(
|
|
165
|
-
result.tx_hashes === undefined || result.tx_hashes === null || Array.isArray(result.tx_hashes),
|
|
166
|
-
"tx_hashes should be array or null"
|
|
167
|
-
);
|
|
168
|
-
})
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
// Flow 9: NameToAddress (test known name "dero" — may not exist on all networks)
|
|
172
|
-
results.push(
|
|
173
|
-
await runFlow("name-to-address", "DERO.NameToAddress — resolve 'dero'", async () => {
|
|
174
|
-
try {
|
|
175
|
-
const result = await deroRpc<{ address?: string; name?: string }>(
|
|
176
|
-
daemonUrl,
|
|
177
|
-
"DERO.NameToAddress",
|
|
178
|
-
{ name: "dero", topoheight: -1 }
|
|
179
|
-
);
|
|
180
|
-
// Name may not be registered, that's okay
|
|
181
|
-
if (result.address) {
|
|
182
|
-
assert(result.address.startsWith("dero") || result.address.startsWith("deto"), "Invalid address format");
|
|
183
|
-
}
|
|
184
|
-
} catch (e) {
|
|
185
|
-
// Name not found is acceptable
|
|
186
|
-
if (String(e).includes("NOT FOUND") || String(e).includes("not found")) {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
throw e;
|
|
190
|
-
}
|
|
191
|
-
})
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
// Flow 10: GetSC — test with the name registry SCID
|
|
195
|
-
const NAME_REGISTRY_SCID = "0000000000000000000000000000000000000000000000000000000000000001";
|
|
196
|
-
results.push(
|
|
197
|
-
await runFlow("get-sc", "DERO.GetSC — name registry contract", async () => {
|
|
198
|
-
const result = await deroRpc<{ code?: string; balances?: unknown }>(
|
|
199
|
-
daemonUrl,
|
|
200
|
-
"DERO.GetSC",
|
|
201
|
-
{ scid: NAME_REGISTRY_SCID, code: true, variables: false }
|
|
202
|
-
);
|
|
203
|
-
assert(result.code, "Missing contract code");
|
|
204
|
-
assert(result.code.includes("Function"), "Code should contain Function keyword");
|
|
205
|
-
})
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
return results;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function formatReport(results: FlowResult[]): string {
|
|
212
|
-
const lines: string[] = [
|
|
213
|
-
"",
|
|
214
|
-
"DERO MCP Flow Test Results",
|
|
215
|
-
"==========================",
|
|
216
|
-
"",
|
|
217
|
-
];
|
|
218
|
-
|
|
219
|
-
const passed = results.filter((r) => r.status === "pass").length;
|
|
220
|
-
const failed = results.filter((r) => r.status === "fail").length;
|
|
221
|
-
const skipped = results.filter((r) => r.status === "skip").length;
|
|
222
|
-
|
|
223
|
-
for (const r of results) {
|
|
224
|
-
const icon = r.status === "pass" ? "✓" : r.status === "fail" ? "✗" : "○";
|
|
225
|
-
const status = r.status.toUpperCase().padEnd(4);
|
|
226
|
-
lines.push(`${icon} ${status} ${r.name} (${r.durationMs}ms)`);
|
|
227
|
-
if (r.message) {
|
|
228
|
-
lines.push(` ${r.message}`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
lines.push("");
|
|
233
|
-
lines.push(`Summary: ${passed} passed, ${failed} failed, ${skipped} skipped`);
|
|
234
|
-
lines.push("");
|
|
235
|
-
|
|
236
|
-
return lines.join("\n");
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
async function main() {
|
|
240
|
-
const daemonUrl = (process.argv[2] || process.env.DERO_DAEMON_URL || DEFAULT_URL).replace(/\/$/, "");
|
|
241
|
-
|
|
242
|
-
console.log(`Testing daemon at: ${daemonUrl}`);
|
|
243
|
-
console.log("");
|
|
244
|
-
|
|
245
|
-
try {
|
|
246
|
-
const results = await runAllFlows(daemonUrl);
|
|
247
|
-
console.log(formatReport(results));
|
|
248
|
-
|
|
249
|
-
const failed = results.filter((r) => r.status === "fail").length;
|
|
250
|
-
process.exit(failed > 0 ? 1 : 0);
|
|
251
|
-
} catch (error) {
|
|
252
|
-
console.error("Fatal error:", error instanceof Error ? error.message : error);
|
|
253
|
-
process.exit(1);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
main();
|