shll-skills 4.0.1 → 5.0.1
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 +27 -1
- package/SKILL.md +22 -0
- package/dist/index.js +66 -0
- package/dist/index.mjs +66 -0
- package/dist/mcp.js +407 -1
- package/dist/mcp.mjs +411 -3
- package/package.json +1 -1
- package/src/index.ts +80 -0
- package/src/mcp.ts +469 -3
package/README.md
CHANGED
|
@@ -65,18 +65,30 @@ RUNNER_PRIVATE_KEY=0x... shll-mcp
|
|
|
65
65
|
|
|
66
66
|
The server communicates via **stdio** using JSON-RPC 2.0. Send `tools/list` to discover all available tools.
|
|
67
67
|
|
|
68
|
-
### Available MCP Tools
|
|
68
|
+
### Available MCP Tools (20 total)
|
|
69
69
|
|
|
70
70
|
| Tool | Type | Description |
|
|
71
71
|
|------|------|-------------|
|
|
72
72
|
| `portfolio` | Read | Vault holdings + token balances |
|
|
73
73
|
| `balance` | Read | Operator wallet gas balance |
|
|
74
74
|
| `price` | Read | Real-time token price (DexScreener) |
|
|
75
|
+
| `search` | Read | Search token by name/symbol on BSC |
|
|
76
|
+
| `tokens` | Read | List known token symbols + addresses |
|
|
75
77
|
| `lending_info` | Read | Venus Protocol supply balances + APY |
|
|
78
|
+
| `policies` | Read | View active on-chain policies + config |
|
|
79
|
+
| `status` | Read | One-shot security overview (vault, operator, policies, activity) |
|
|
80
|
+
| `history` | Read | Recent transactions + policy rejections |
|
|
81
|
+
| `my_agents` | Read | List agents where current operator is authorized |
|
|
82
|
+
| `listings` | Read | Available agent templates for rent |
|
|
76
83
|
| `swap` | Write | PancakeSwap V2/V3 auto-routing swap |
|
|
84
|
+
| `wrap` | Write | BNB → WBNB in vault |
|
|
85
|
+
| `unwrap` | Write | WBNB → BNB in vault |
|
|
77
86
|
| `lend` | Write | Supply tokens to Venus for yield |
|
|
78
87
|
| `redeem` | Write | Withdraw from Venus |
|
|
79
88
|
| `transfer` | Write | Send BNB or ERC20 from vault |
|
|
89
|
+
| `config` | Write | Configure risk parameters (spending limits, cooldown) |
|
|
90
|
+
| `setup_guide` | Info | Generate dual-wallet onboarding URL + steps |
|
|
91
|
+
| `generate_wallet` | Info | Create new operator wallet (address + key) |
|
|
80
92
|
|
|
81
93
|
---
|
|
82
94
|
|
|
@@ -169,6 +181,20 @@ AI Agent -> CLI/MCP -> PolicyClient.validate() -> PolicyGuard (on-chain) -> vaul
|
|
|
169
181
|
- npm: [shll-skills](https://www.npmjs.com/package/shll-skills)
|
|
170
182
|
- GitHub: [kledx/shll-skills](https://github.com/kledx/shll-skills)
|
|
171
183
|
|
|
184
|
+
## 🧩 Multi-Skill Compatibility
|
|
185
|
+
|
|
186
|
+
SHLL can coexist with other DeFi skills (OKX DEX API, Bitget Wallet, etc.). Key architectural differences:
|
|
187
|
+
|
|
188
|
+
| | **SHLL** | **OKX DEX API** | **Bitget Wallet** |
|
|
189
|
+
|---|---|---|---|
|
|
190
|
+
| **Wallet** | Smart contract vault (AgentNFA) | User EOA | Bitget custody |
|
|
191
|
+
| **Execution** | On-chain via PolicyGuard | Calldata only (user signs) | HMAC API |
|
|
192
|
+
| **Safety** | On-chain policy enforcement | User approval | API key perms |
|
|
193
|
+
| **AI autonomy** | Execute within policy limits | Cannot execute | Full API access |
|
|
194
|
+
| **Risk if key leaked** | Policy-limited trades only | N/A | Full API access |
|
|
195
|
+
|
|
196
|
+
**SHLL is the only skill with on-chain policy enforcement.** Even if the AI hallucinates, the smart contract rejects unsafe operations.
|
|
197
|
+
|
|
172
198
|
## License
|
|
173
199
|
|
|
174
200
|
MIT
|
package/SKILL.md
CHANGED
|
@@ -26,6 +26,7 @@ These rules are **non-negotiable**. Violating any of them is a critical failure.
|
|
|
26
26
|
4. **One agent per conversation.** Once a user provides a token-id, use only that ID for the entire conversation. If they want to switch, they must explicitly say so.
|
|
27
27
|
5. **Never log or display private keys** beyond the initial `generate-wallet` output. If the user asks you to repeat it, remind them to check their saved copy.
|
|
28
28
|
6. **Do not infer trading intent.** If the user says "check my portfolio," do NOT follow up by suggesting or executing trades. Only trade when the user explicitly asks.
|
|
29
|
+
7. **Multi-skill coexistence.** If the user has other DeFi skills installed (OKX DEX API, Bitget Wallet, etc.), **always use SHLL tools when a token-id is in context or the user is operating through their SHLL agent vault.** Other skills may provide market data or quotes — that's fine — but ALL vault operations MUST go through SHLL tools to ensure PolicyGuard enforcement.
|
|
29
30
|
|
|
30
31
|
---
|
|
31
32
|
|
|
@@ -229,6 +230,27 @@ All commands output JSON:
|
|
|
229
230
|
- Rejected by policy: `{"status":"rejected", "reason":"Exceeds per-tx limit"}`
|
|
230
231
|
- Error: `{"status":"error", "message":"..."}`
|
|
231
232
|
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## 🧩 MULTI-SKILL COMPATIBILITY: How SHLL Differs from Other DeFi Skills
|
|
236
|
+
|
|
237
|
+
If the user has multiple DeFi skills installed, understand the architectural differences:
|
|
238
|
+
|
|
239
|
+
| | **SHLL** | **OKX DEX API** | **Bitget Wallet Skill** |
|
|
240
|
+
|---|---|---|---|
|
|
241
|
+
| **Wallet model** | Smart contract vault (AgentNFA) | User's EOA wallet | Bitget API custody |
|
|
242
|
+
| **Execution** | On-chain via PolicyGuard | Generates calldata → user signs | HMAC API call → Bitget backend |
|
|
243
|
+
| **Safety** | On-chain policy enforcement (spending limits, cooldowns, DEX whitelist) | User approval only | API key permissions |
|
|
244
|
+
| **Fund location** | On-chain vault (isolated per agent) | User's wallet directly | Bitget platform |
|
|
245
|
+
| **AI autonomy** | Can execute within policy limits | Cannot execute (calldata only) | Can execute via API |
|
|
246
|
+
| **Risk if key leaked** | Limited to policy-allowed trades | N/A (no key) | Full API access |
|
|
247
|
+
|
|
248
|
+
**Key distinction:** SHLL is the only skill with **on-chain policy enforcement**. Even if the AI makes a mistake, the smart contract rejects operations that violate spending limits or cooldowns. Other skills rely on the user or platform to gatekeep.
|
|
249
|
+
|
|
250
|
+
**Routing rule:** When the user's intent involves their SHLL agent vault (identified by token-id), ALWAYS use SHLL tools. It's fine to use other skills for price quotes, market research, or operations outside SHLL.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
232
254
|
## LINKS
|
|
233
255
|
- Website: https://shll.run
|
|
234
256
|
- Twitter: @shllrun
|
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);
|