shll-skills 4.0.0 → 5.0.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 +109 -60
- package/dist/index.js +66 -0
- package/dist/index.mjs +66 -0
- package/dist/mcp.js +408 -4
- package/dist/mcp.mjs +412 -6
- package/package.json +1 -1
- package/src/index.ts +80 -0
- package/src/mcp.ts +472 -6
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://shll.run) [](https://twitter.com/shllrun) [](https://www.npmjs.com/package/shll-skills)
|
|
4
4
|
|
|
5
|
-
A CLI toolkit that gives
|
|
5
|
+
A **CLI + MCP Server** toolkit that gives any AI agent the ability to execute DeFi operations on BSC Mainnet securely. Supports PancakeSwap V2/V3 swap routing, Venus Protocol lending, and more. All transactions are validated by the on-chain PolicyGuard — even if the AI hallucinates, the contract rejects unsafe operations.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -10,81 +10,141 @@ A CLI toolkit that gives **any AI agent** (OpenClaw, Claude, Codex, ChatGPT, etc
|
|
|
10
10
|
npm install -g shll-skills
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
This installs two binaries:
|
|
14
|
+
- `shll-run` — CLI mode (for OpenClaw, shell scripts, etc.)
|
|
15
|
+
- `shll-mcp` — MCP Server mode (for Claude, Cursor, Gemini, etc.)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 🔌 MCP Server Setup (Recommended for AI Agents)
|
|
20
|
+
|
|
21
|
+
The [Model Context Protocol](https://modelcontextprotocol.io/) lets AI agents discover and call SHLL tools natively — no CLI parsing needed.
|
|
22
|
+
|
|
23
|
+
### Claude Desktop
|
|
24
|
+
|
|
25
|
+
Edit `~/AppData/Roaming/Claude/claude_desktop_config.json` (Windows) or `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"shll-defi": {
|
|
31
|
+
"command": "shll-mcp",
|
|
32
|
+
"env": {
|
|
33
|
+
"RUNNER_PRIVATE_KEY": "0x_YOUR_OPERATOR_PRIVATE_KEY"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Restart Claude Desktop. You'll see SHLL tools appear in the 🔧 menu.
|
|
41
|
+
|
|
42
|
+
### Cursor
|
|
43
|
+
|
|
44
|
+
Create `.cursor/mcp.json` in your project root:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"shll-defi": {
|
|
50
|
+
"command": "npx",
|
|
51
|
+
"args": ["-y", "shll-skills", "--mcp"],
|
|
52
|
+
"env": {
|
|
53
|
+
"RUNNER_PRIVATE_KEY": "0x_YOUR_OPERATOR_PRIVATE_KEY"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Custom Agent (programmatic)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
RUNNER_PRIVATE_KEY=0x... shll-mcp
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The server communicates via **stdio** using JSON-RPC 2.0. Send `tools/list` to discover all available tools.
|
|
67
|
+
|
|
68
|
+
### Available MCP Tools
|
|
69
|
+
|
|
70
|
+
| Tool | Type | Description |
|
|
71
|
+
|------|------|-------------|
|
|
72
|
+
| `portfolio` | Read | Vault holdings + token balances |
|
|
73
|
+
| `balance` | Read | Operator wallet gas balance |
|
|
74
|
+
| `price` | Read | Real-time token price (DexScreener) |
|
|
75
|
+
| `lending_info` | Read | Venus Protocol supply balances + APY |
|
|
76
|
+
| `swap` | Write | PancakeSwap V2/V3 auto-routing swap |
|
|
77
|
+
| `lend` | Write | Supply tokens to Venus for yield |
|
|
78
|
+
| `redeem` | Write | Withdraw from Venus |
|
|
79
|
+
| `transfer` | Write | Send BNB or ERC20 from vault |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 📟 CLI Mode
|
|
84
|
+
|
|
85
|
+
For OpenClaw, shell scripts, or manual use.
|
|
86
|
+
|
|
87
|
+
### Quick Start
|
|
14
88
|
|
|
15
89
|
```bash
|
|
16
90
|
# 1. Generate an operator wallet (hot wallet for AI)
|
|
17
91
|
shll-run generate-wallet
|
|
18
|
-
|
|
92
|
+
export RUNNER_PRIVATE_KEY="0x..."
|
|
19
93
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
# 2. Get setup instructions (user completes on shll.run with their OWN wallet)
|
|
94
|
+
# 2. Get setup link (user completes on shll.run with their OWN wallet)
|
|
23
95
|
shll-run setup-guide --listing-id 0xABC...DEF --days 30
|
|
24
|
-
# -> Outputs shll.run link for rent + authorize + fund
|
|
25
96
|
|
|
26
|
-
# 3.
|
|
27
|
-
shll-run swap --from BNB --to USDC --amount 0.1
|
|
97
|
+
# 3. Trade
|
|
98
|
+
shll-run swap --from BNB --to USDC --amount 0.1 -k 5
|
|
28
99
|
```
|
|
29
100
|
|
|
30
|
-
|
|
101
|
+
### Commands
|
|
31
102
|
|
|
32
|
-
|
|
103
|
+
#### Trading
|
|
33
104
|
```bash
|
|
34
|
-
shll-run swap
|
|
35
|
-
shll-run
|
|
36
|
-
shll-run
|
|
37
|
-
shll-run
|
|
38
|
-
shll-run
|
|
105
|
+
shll-run swap -f <FROM> -t <TO> -a <AMT> -k <ID> # Auto V2/V3 routing
|
|
106
|
+
shll-run swap -f BNB -t USDT -a 0.1 -k 5 --dex v3 --fee 500 # Force V3, 0.05% fee
|
|
107
|
+
shll-run wrap -a <BNB> -k <ID> # BNB -> WBNB
|
|
108
|
+
shll-run unwrap -a <BNB> -k <ID> # WBNB -> BNB
|
|
109
|
+
shll-run transfer --token <SYM> -a <AMT> --to <ADDR> -k <ID>
|
|
39
110
|
```
|
|
40
111
|
|
|
41
|
-
|
|
112
|
+
#### Lending (Venus Protocol)
|
|
113
|
+
```bash
|
|
114
|
+
shll-run lend -t USDT -a 100 -k <ID> # Supply to Venus
|
|
115
|
+
shll-run redeem -t USDT -a 50 -k <ID> # Withdraw from Venus
|
|
116
|
+
shll-run lending-info -k <ID> # Show APY + positions
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### Market Data (read-only)
|
|
42
120
|
```bash
|
|
43
121
|
shll-run portfolio -k <ID> # Vault holdings + USD values
|
|
44
122
|
shll-run price --token <SYM> # Real-time price (DexScreener)
|
|
45
|
-
shll-run search --query <TEXT> # Find token by name
|
|
46
|
-
shll-run tokens # List known
|
|
123
|
+
shll-run search --query <TEXT> # Find token by name
|
|
124
|
+
shll-run tokens # List known tokens
|
|
47
125
|
```
|
|
48
126
|
|
|
49
|
-
|
|
127
|
+
#### Risk Management
|
|
50
128
|
```bash
|
|
51
|
-
shll-run policies -k <ID> # View active policies
|
|
129
|
+
shll-run policies -k <ID> # View active on-chain policies
|
|
52
130
|
shll-run config -k <ID> --tx-limit <BNB> --daily-limit <BNB> --cooldown <SEC>
|
|
53
131
|
```
|
|
54
132
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
This skill outputs **structured JSON** on stdout, making it easy for any AI agent to parse:
|
|
58
|
-
|
|
59
|
-
```json
|
|
60
|
-
{"status":"success","tx":"0xabc...","message":"Swapped 0.1 BNB -> 12.5 USDC"}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
```json
|
|
64
|
-
{"status":"rejected","reason":"Spending limit exceeded"}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### For AI providers:
|
|
68
|
-
- **SKILL.md** — Structured skill metadata (name, description, commands, install instructions)
|
|
69
|
-
- **stdout** — JSON-only output, designed for programmatic parsing
|
|
70
|
-
- **stderr** — Human-readable errors
|
|
71
|
-
- **Exit codes** — `0` = success, `1` = failure
|
|
133
|
+
---
|
|
72
134
|
|
|
73
135
|
## How It Works
|
|
74
136
|
|
|
75
137
|
```
|
|
76
|
-
AI Agent -> CLI
|
|
138
|
+
AI Agent -> CLI/MCP -> PolicyClient.validate() -> PolicyGuard (on-chain) -> vault
|
|
77
139
|
```
|
|
78
140
|
|
|
79
|
-
1. AI constructs a
|
|
141
|
+
1. AI constructs a tool call (MCP) or CLI command
|
|
80
142
|
2. `PolicyClient.validate()` simulates against all on-chain policies
|
|
81
|
-
3. If approved, `AgentNFA.execute()` routes through PolicyGuard
|
|
82
|
-
4. PolicyGuard enforces: spending limits, cooldowns, DEX whitelist,
|
|
143
|
+
3. If approved, `AgentNFA.execute()` routes through PolicyGuard → vault
|
|
144
|
+
4. PolicyGuard enforces: spending limits, cooldowns, DEX whitelist, DeFi guard
|
|
83
145
|
|
|
84
146
|
## Security: Dual-Wallet Architecture
|
|
85
147
|
|
|
86
|
-
SHLL enforces **separation of owner and operator wallets**:
|
|
87
|
-
|
|
88
148
|
| | Owner Wallet | Operator Wallet (RUNNER_PRIVATE_KEY) |
|
|
89
149
|
|---|---|---|
|
|
90
150
|
| **Who holds it** | User (MetaMask/hardware) | AI agent |
|
|
@@ -93,26 +153,14 @@ SHLL enforces **separation of owner and operator wallets**:
|
|
|
93
153
|
| **Can transfer NFT** | ✅ | ❌ |
|
|
94
154
|
| **Risk if leaked** | 🚨 Full vault access | ⚠️ Limited to policy-allowed trades |
|
|
95
155
|
|
|
96
|
-
**Additional on-chain enforcement:**
|
|
97
|
-
- PolicyGuard validates every transaction, not the AI
|
|
98
|
-
- Vault isolation — operator key cannot directly access vault funds
|
|
99
|
-
- Risk limits can only be tightened, never loosened
|
|
100
|
-
- Unknown selectors, targets, or recipients are rejected
|
|
101
|
-
|
|
102
156
|
## Environment Variables
|
|
103
157
|
|
|
104
158
|
| Variable | Required | Description |
|
|
105
159
|
|----------|----------|-------------|
|
|
106
|
-
| `RUNNER_PRIVATE_KEY` | Yes | Operator wallet key (
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
|
|
111
|
-
## Updating
|
|
112
|
-
|
|
113
|
-
```bash
|
|
114
|
-
npm update -g shll-skills
|
|
115
|
-
```
|
|
160
|
+
| `RUNNER_PRIVATE_KEY` | Yes | Operator wallet key (~$1 BNB for gas) |
|
|
161
|
+
| `SHLL_RPC` | No | BSC RPC URL override |
|
|
162
|
+
| `SHLL_NFA` | No | AgentNFA contract override |
|
|
163
|
+
| `SHLL_GUARD` | No | PolicyGuard contract override |
|
|
116
164
|
|
|
117
165
|
## Links
|
|
118
166
|
|
|
@@ -124,3 +172,4 @@ npm update -g shll-skills
|
|
|
124
172
|
## License
|
|
125
173
|
|
|
126
174
|
MIT
|
|
175
|
+
|
package/dist/index.js
CHANGED
|
@@ -2152,6 +2152,71 @@ lendingInfoCmd.action(async (opts) => {
|
|
|
2152
2152
|
process.exit(1);
|
|
2153
2153
|
}
|
|
2154
2154
|
});
|
|
2155
|
+
var OPERATOR_OF_ABI = [{
|
|
2156
|
+
type: "function",
|
|
2157
|
+
name: "operatorOf",
|
|
2158
|
+
inputs: [{ name: "tokenId", type: "uint256" }],
|
|
2159
|
+
outputs: [{ name: "", type: "address" }],
|
|
2160
|
+
stateMutability: "view"
|
|
2161
|
+
}];
|
|
2162
|
+
var myAgentsCmd = new import_commander.Command("my-agents").description("List all agents where the current RUNNER_PRIVATE_KEY is the operator").option("-r, --rpc <url>", "BSC RPC URL", DEFAULT_RPC).option("--nfa-address <address>", "AgentNFA contract address", DEFAULT_NFA).option("--indexer <url>", "Indexer base URL", DEFAULT_INDEXER);
|
|
2163
|
+
myAgentsCmd.action(async (opts) => {
|
|
2164
|
+
try {
|
|
2165
|
+
if (!process.env.RUNNER_PRIVATE_KEY) {
|
|
2166
|
+
output({ status: "error", message: "RUNNER_PRIVATE_KEY environment variable is missing" });
|
|
2167
|
+
process.exit(1);
|
|
2168
|
+
}
|
|
2169
|
+
const operator = (0, import_accounts2.privateKeyToAccount)(toHex(process.env.RUNNER_PRIVATE_KEY)).address.toLowerCase();
|
|
2170
|
+
const rpcUrl = opts.rpc || DEFAULT_RPC;
|
|
2171
|
+
const nfaAddr = toHex(opts.nfaAddress || DEFAULT_NFA);
|
|
2172
|
+
const indexerUrl = (opts.indexer || DEFAULT_INDEXER).replace(/\/+$/, "");
|
|
2173
|
+
const publicClient = (0, import_viem2.createPublicClient)({ chain: import_chains2.bsc, transport: (0, import_viem2.http)(rpcUrl) });
|
|
2174
|
+
const res = await fetch(`${indexerUrl}/api/agents`);
|
|
2175
|
+
if (!res.ok) {
|
|
2176
|
+
output({ status: "error", message: `Indexer error: ${res.status}` });
|
|
2177
|
+
process.exit(1);
|
|
2178
|
+
}
|
|
2179
|
+
const json = await res.json();
|
|
2180
|
+
const agents = (json.items || []).filter((a) => !a.isTemplate && a.tokenId !== void 0);
|
|
2181
|
+
if (agents.length === 0) {
|
|
2182
|
+
output({ status: "success", agents: [], message: "No agents found in indexer" });
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
const checks = await Promise.all(
|
|
2186
|
+
agents.map(async (a) => {
|
|
2187
|
+
const tokenId = BigInt(a.tokenId);
|
|
2188
|
+
try {
|
|
2189
|
+
const op = await publicClient.readContract({
|
|
2190
|
+
address: nfaAddr,
|
|
2191
|
+
abi: OPERATOR_OF_ABI,
|
|
2192
|
+
functionName: "operatorOf",
|
|
2193
|
+
args: [tokenId]
|
|
2194
|
+
});
|
|
2195
|
+
return {
|
|
2196
|
+
tokenId: tokenId.toString(),
|
|
2197
|
+
vault: a.account || "",
|
|
2198
|
+
owner: a.owner || "",
|
|
2199
|
+
agentType: a.agentType || "unknown",
|
|
2200
|
+
isOperator: op.toLowerCase() === operator
|
|
2201
|
+
};
|
|
2202
|
+
} catch {
|
|
2203
|
+
return null;
|
|
2204
|
+
}
|
|
2205
|
+
})
|
|
2206
|
+
);
|
|
2207
|
+
const myAgents = checks.filter((c) => c !== null && c.isOperator);
|
|
2208
|
+
output({
|
|
2209
|
+
status: "success",
|
|
2210
|
+
operator,
|
|
2211
|
+
agents: myAgents,
|
|
2212
|
+
count: myAgents.length
|
|
2213
|
+
});
|
|
2214
|
+
} catch (error) {
|
|
2215
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2216
|
+
output({ status: "error", message });
|
|
2217
|
+
process.exit(1);
|
|
2218
|
+
}
|
|
2219
|
+
});
|
|
2155
2220
|
program.addCommand(swapCmd);
|
|
2156
2221
|
program.addCommand(rawCmd);
|
|
2157
2222
|
program.addCommand(tokensCmd);
|
|
@@ -2173,4 +2238,5 @@ program.addCommand(statusCmd);
|
|
|
2173
2238
|
program.addCommand(lendCmd);
|
|
2174
2239
|
program.addCommand(redeemCmd);
|
|
2175
2240
|
program.addCommand(lendingInfoCmd);
|
|
2241
|
+
program.addCommand(myAgentsCmd);
|
|
2176
2242
|
program.parse(process.argv);
|
package/dist/index.mjs
CHANGED
|
@@ -1664,6 +1664,71 @@ lendingInfoCmd.action(async (opts) => {
|
|
|
1664
1664
|
process.exit(1);
|
|
1665
1665
|
}
|
|
1666
1666
|
});
|
|
1667
|
+
var OPERATOR_OF_ABI = [{
|
|
1668
|
+
type: "function",
|
|
1669
|
+
name: "operatorOf",
|
|
1670
|
+
inputs: [{ name: "tokenId", type: "uint256" }],
|
|
1671
|
+
outputs: [{ name: "", type: "address" }],
|
|
1672
|
+
stateMutability: "view"
|
|
1673
|
+
}];
|
|
1674
|
+
var myAgentsCmd = new Command("my-agents").description("List all agents where the current RUNNER_PRIVATE_KEY is the operator").option("-r, --rpc <url>", "BSC RPC URL", DEFAULT_RPC).option("--nfa-address <address>", "AgentNFA contract address", DEFAULT_NFA).option("--indexer <url>", "Indexer base URL", DEFAULT_INDEXER);
|
|
1675
|
+
myAgentsCmd.action(async (opts) => {
|
|
1676
|
+
try {
|
|
1677
|
+
if (!process.env.RUNNER_PRIVATE_KEY) {
|
|
1678
|
+
output({ status: "error", message: "RUNNER_PRIVATE_KEY environment variable is missing" });
|
|
1679
|
+
process.exit(1);
|
|
1680
|
+
}
|
|
1681
|
+
const operator = privateKeyToAccount(toHex(process.env.RUNNER_PRIVATE_KEY)).address.toLowerCase();
|
|
1682
|
+
const rpcUrl = opts.rpc || DEFAULT_RPC;
|
|
1683
|
+
const nfaAddr = toHex(opts.nfaAddress || DEFAULT_NFA);
|
|
1684
|
+
const indexerUrl = (opts.indexer || DEFAULT_INDEXER).replace(/\/+$/, "");
|
|
1685
|
+
const publicClient = createPublicClient({ chain: bsc, transport: http(rpcUrl) });
|
|
1686
|
+
const res = await fetch(`${indexerUrl}/api/agents`);
|
|
1687
|
+
if (!res.ok) {
|
|
1688
|
+
output({ status: "error", message: `Indexer error: ${res.status}` });
|
|
1689
|
+
process.exit(1);
|
|
1690
|
+
}
|
|
1691
|
+
const json = await res.json();
|
|
1692
|
+
const agents = (json.items || []).filter((a) => !a.isTemplate && a.tokenId !== void 0);
|
|
1693
|
+
if (agents.length === 0) {
|
|
1694
|
+
output({ status: "success", agents: [], message: "No agents found in indexer" });
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
const checks = await Promise.all(
|
|
1698
|
+
agents.map(async (a) => {
|
|
1699
|
+
const tokenId = BigInt(a.tokenId);
|
|
1700
|
+
try {
|
|
1701
|
+
const op = await publicClient.readContract({
|
|
1702
|
+
address: nfaAddr,
|
|
1703
|
+
abi: OPERATOR_OF_ABI,
|
|
1704
|
+
functionName: "operatorOf",
|
|
1705
|
+
args: [tokenId]
|
|
1706
|
+
});
|
|
1707
|
+
return {
|
|
1708
|
+
tokenId: tokenId.toString(),
|
|
1709
|
+
vault: a.account || "",
|
|
1710
|
+
owner: a.owner || "",
|
|
1711
|
+
agentType: a.agentType || "unknown",
|
|
1712
|
+
isOperator: op.toLowerCase() === operator
|
|
1713
|
+
};
|
|
1714
|
+
} catch {
|
|
1715
|
+
return null;
|
|
1716
|
+
}
|
|
1717
|
+
})
|
|
1718
|
+
);
|
|
1719
|
+
const myAgents = checks.filter((c) => c !== null && c.isOperator);
|
|
1720
|
+
output({
|
|
1721
|
+
status: "success",
|
|
1722
|
+
operator,
|
|
1723
|
+
agents: myAgents,
|
|
1724
|
+
count: myAgents.length
|
|
1725
|
+
});
|
|
1726
|
+
} catch (error) {
|
|
1727
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1728
|
+
output({ status: "error", message });
|
|
1729
|
+
process.exit(1);
|
|
1730
|
+
}
|
|
1731
|
+
});
|
|
1667
1732
|
program.addCommand(swapCmd);
|
|
1668
1733
|
program.addCommand(rawCmd);
|
|
1669
1734
|
program.addCommand(tokensCmd);
|
|
@@ -1685,4 +1750,5 @@ program.addCommand(statusCmd);
|
|
|
1685
1750
|
program.addCommand(lendCmd);
|
|
1686
1751
|
program.addCommand(redeemCmd);
|
|
1687
1752
|
program.addCommand(lendingInfoCmd);
|
|
1753
|
+
program.addCommand(myAgentsCmd);
|
|
1688
1754
|
program.parse(process.argv);
|