@cyberpay/nano-mcp 0.1.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 +105 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +272 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# @cyberpay/nano-mcp
|
|
2
|
+
|
|
3
|
+
MCP Server for CyberNanoPay — 让 AI Agent 拥有零 Gas 纳米支付能力。
|
|
4
|
+
|
|
5
|
+
## 为什么需要这个?
|
|
6
|
+
|
|
7
|
+
传统支付方式无法满足 AI Agent 的高频微付需求:
|
|
8
|
+
|
|
9
|
+
| 收费模式 | 问题 |
|
|
10
|
+
|---------|------|
|
|
11
|
+
| 订阅制 | Agent 为了调 3 次 API 得买一个月,用量不可预测 |
|
|
12
|
+
| 积分/预付包 | 买 1000 次用了 50 次就不来了,剩余额度浪费 |
|
|
13
|
+
| 信用卡按次扣 | Stripe 最低手续费 $0.30/笔,$0.001 的 API 调用根本不可能按次收 |
|
|
14
|
+
| 链上直接付 | 每笔交易有 Gas 成本,高频场景(每秒几十次)不现实 |
|
|
15
|
+
|
|
16
|
+
CyberNanoPay 通过 offchain Ed25519 签名 + TEE 聚合 + 链上批量结算,让单笔支付成本趋近于零,$0.0001 的微付也经济可行。
|
|
17
|
+
|
|
18
|
+
**对 API 商家来说,这意味着除了订阅和积分包,多了一种之前技术上做不到的收费方式 — 真正的按次收费。**
|
|
19
|
+
|
|
20
|
+
## 覆盖场景
|
|
21
|
+
|
|
22
|
+
NanoPay 解锁了这些之前"链上按次付不现实"的高频场景:
|
|
23
|
+
|
|
24
|
+
| 场景 | 单次价值 | 频率 | NanoPay 方案 |
|
|
25
|
+
|------|---------|------|-------------|
|
|
26
|
+
| LLM 推理 / 图片生成 | $0.01–$0.50 | 中频 | offchain 签名即时付,高价值场景 |
|
|
27
|
+
| RPC 节点 / 链上数据查询 | $0.0001 | 每秒几十次 | TEE 链下记账,批量结算 |
|
|
28
|
+
| AI Crawlers / RAG 检索 | $0.001/页 | 日均 10 万次 | x402 协议自动付费,定期结算 |
|
|
29
|
+
| IoT 传感器 / 充电桩 / WiFi | $0.0001–$0.01 | 每秒/每分钟 | offchain 签名累积,定时结算 |
|
|
30
|
+
| 付费文章 / 学术论文 | $0.10 | 低频 | 替代 $30 订阅,按篇付费 |
|
|
31
|
+
| 短视频微付 | $0.001 | 每次观看 | 用户无感,TEE 记账 |
|
|
32
|
+
| GPU 算力租用 | $0.01/秒 | 持续 | 按秒计费,批量结算 |
|
|
33
|
+
| 预言机喂价 / DEX 报价 | $0.001 | 高频 | 链上生态天然打通 |
|
|
34
|
+
|
|
35
|
+
## 与 @ton/mcp 的协作
|
|
36
|
+
|
|
37
|
+
`@cyberpay/nano-mcp` 和 `@ton/mcp`(TON Agentic Wallets)是互补关系:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
@ton/mcp (链上操作层)
|
|
41
|
+
├── 充值 USDT 到 CyberGateway 合约(一次性)
|
|
42
|
+
├── 查链上余额、交易状态
|
|
43
|
+
├── 提款、swap、NFT 操作
|
|
44
|
+
└── 管理 Agentic Wallet
|
|
45
|
+
|
|
46
|
+
@cyberpay/nano-mcp (高频微付层)
|
|
47
|
+
├── offchain 签名支付(零 Gas,~1ms)
|
|
48
|
+
├── TEE 验证 + 记账
|
|
49
|
+
├── 批量结算到链上
|
|
50
|
+
└── TEE 签名收据 + Merkle 证明
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
AI Agent 的完整支付流程:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
1. Agent 通过 @ton/mcp → 充值 USDT 到 CyberGateway(链上,一次性)
|
|
57
|
+
2. Agent 通过 @cyberpay/nano-mcp → offchain 签名付费(每次 API 调用,零 Gas)
|
|
58
|
+
3. TEE 聚合器 → 验签、记账、累积批次
|
|
59
|
+
4. TEE → 定期批量结算到链上(bilateral netting 压缩交易数)
|
|
60
|
+
5. Agent 通过 @ton/mcp → 查余额、查结算状态、需要时再充值
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 安装
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx @cyberpay/nano-mcp
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## MCP 配置
|
|
70
|
+
|
|
71
|
+
添加到 MCP 设置文件(`mcp.json`):
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"mcpServers": {
|
|
76
|
+
"nano-pay": {
|
|
77
|
+
"command": "npx",
|
|
78
|
+
"args": ["@cyberpay/nano-mcp"],
|
|
79
|
+
"env": {
|
|
80
|
+
"NANO_TEE_URL": "https://tee.cyberpay.org"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
搭配 TON Agentic Wallet 使用(推荐):
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"mcpServers": {
|
|
92
|
+
"ton": {
|
|
93
|
+
"command": "npx",
|
|
94
|
+
"args": ["-y", "@ton/mcp@alpha"]
|
|
95
|
+
},
|
|
96
|
+
"nano-pay": {
|
|
97
|
+
"command": "npx",
|
|
98
|
+
"args": ["@cyberpay/nano-mcp"],
|
|
99
|
+
"env": {
|
|
100
|
+
"NANO_TEE_URL": "https://tee.cyberpay.org"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CyberNanoPay MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Gives AI agents gas-free nanopayment capabilities on TON.
|
|
6
|
+
* Works alongside Agentic Wallets — agent uses agentic wallet for on-chain ops,
|
|
7
|
+
* and this MCP for high-frequency micropayments.
|
|
8
|
+
*
|
|
9
|
+
* Tools:
|
|
10
|
+
* nano_deposit — Deposit funds (simulated or on-chain)
|
|
11
|
+
* nano_pay — Sign and submit a micropayment
|
|
12
|
+
* nano_balance — Check balance
|
|
13
|
+
* nano_history — View payment history
|
|
14
|
+
* nano_receipt — Get a TEE-signed receipt
|
|
15
|
+
* nano_attestation — Verify TEE attestation
|
|
16
|
+
* nano_flush — Trigger batch settlement (settle pending payments on-chain)
|
|
17
|
+
* nano_withdraw — Request withdrawal (flush unsettled → prepare for on-chain withdraw)
|
|
18
|
+
* nano_policy — Get or set spending policy (limits, daily cap, HITL threshold)
|
|
19
|
+
*/
|
|
20
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CyberNanoPay MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Gives AI agents gas-free nanopayment capabilities on TON.
|
|
6
|
+
* Works alongside Agentic Wallets — agent uses agentic wallet for on-chain ops,
|
|
7
|
+
* and this MCP for high-frequency micropayments.
|
|
8
|
+
*
|
|
9
|
+
* Tools:
|
|
10
|
+
* nano_deposit — Deposit funds (simulated or on-chain)
|
|
11
|
+
* nano_pay — Sign and submit a micropayment
|
|
12
|
+
* nano_balance — Check balance
|
|
13
|
+
* nano_history — View payment history
|
|
14
|
+
* nano_receipt — Get a TEE-signed receipt
|
|
15
|
+
* nano_attestation — Verify TEE attestation
|
|
16
|
+
* nano_flush — Trigger batch settlement (settle pending payments on-chain)
|
|
17
|
+
* nano_withdraw — Request withdrawal (flush unsettled → prepare for on-chain withdraw)
|
|
18
|
+
* nano_policy — Get or set spending policy (limits, daily cap, HITL threshold)
|
|
19
|
+
*/
|
|
20
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
21
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
22
|
+
import { z } from "zod";
|
|
23
|
+
import nacl from "tweetnacl";
|
|
24
|
+
import { createHash } from "crypto";
|
|
25
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
26
|
+
import { join } from "path";
|
|
27
|
+
import { homedir } from "os";
|
|
28
|
+
const TEE_URL = process.env.NANO_TEE_URL ?? "https://3c84244ec8585d9d81678e9f8933c2b63bbfe5cd-4030.dstack-pha-prod5.phala.network";
|
|
29
|
+
const ADMIN_TOKEN = process.env.NANO_ADMIN_TOKEN ?? "";
|
|
30
|
+
// ── Keypair management ──
|
|
31
|
+
// Agent generates or loads a keypair for signing payments
|
|
32
|
+
// Seed is persisted to ~/.cyberpay/agent-seed so address survives restarts
|
|
33
|
+
const SEED_DIR = join(homedir(), ".cyberpay");
|
|
34
|
+
const SEED_FILE = join(SEED_DIR, "agent-seed");
|
|
35
|
+
let agentKeypair = null;
|
|
36
|
+
function getOrCreateKeypair() {
|
|
37
|
+
if (agentKeypair)
|
|
38
|
+
return agentKeypair;
|
|
39
|
+
// Priority 1: env var
|
|
40
|
+
const envSeed = process.env.NANO_AGENT_SEED;
|
|
41
|
+
if (envSeed) {
|
|
42
|
+
agentKeypair = nacl.sign.keyPair.fromSeed(Buffer.from(envSeed, "hex"));
|
|
43
|
+
return agentKeypair;
|
|
44
|
+
}
|
|
45
|
+
// Priority 2: persisted seed file
|
|
46
|
+
try {
|
|
47
|
+
const saved = readFileSync(SEED_FILE, "utf-8").trim();
|
|
48
|
+
if (saved.length === 64) {
|
|
49
|
+
agentKeypair = nacl.sign.keyPair.fromSeed(Buffer.from(saved, "hex"));
|
|
50
|
+
return agentKeypair;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// File doesn't exist yet — will create below
|
|
55
|
+
}
|
|
56
|
+
// Priority 3: generate new and persist
|
|
57
|
+
const seed = nacl.randomBytes(32);
|
|
58
|
+
agentKeypair = nacl.sign.keyPair.fromSeed(seed);
|
|
59
|
+
try {
|
|
60
|
+
mkdirSync(SEED_DIR, { recursive: true });
|
|
61
|
+
writeFileSync(SEED_FILE, Buffer.from(seed).toString("hex"), { mode: 0o600 });
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Non-fatal: agent works but address changes on restart
|
|
65
|
+
}
|
|
66
|
+
return agentKeypair;
|
|
67
|
+
}
|
|
68
|
+
function toHex(bytes) {
|
|
69
|
+
return Buffer.from(bytes).toString("hex");
|
|
70
|
+
}
|
|
71
|
+
function agentAddress() {
|
|
72
|
+
const kp = getOrCreateKeypair();
|
|
73
|
+
return "0:" + toHex(kp.publicKey.slice(0, 32));
|
|
74
|
+
}
|
|
75
|
+
// ── Payment signing ──
|
|
76
|
+
function signPayment(to, amount, nonce, validBefore) {
|
|
77
|
+
const kp = getOrCreateKeypair();
|
|
78
|
+
const prefix = Buffer.from("CyberGateway:v1:");
|
|
79
|
+
const fromHash = Buffer.from(kp.publicKey.slice(0, 32));
|
|
80
|
+
// Parse "to" address hash
|
|
81
|
+
const toClean = to.startsWith("0:") ? to.slice(2) : to;
|
|
82
|
+
const toHash = Buffer.from(toClean.padEnd(64, "0").slice(0, 64), "hex");
|
|
83
|
+
const buf = Buffer.alloc(prefix.length + 32 + 32 + 16 + 8 + 32);
|
|
84
|
+
let offset = 0;
|
|
85
|
+
prefix.copy(buf, offset);
|
|
86
|
+
offset += prefix.length;
|
|
87
|
+
fromHash.copy(buf, offset);
|
|
88
|
+
offset += 32;
|
|
89
|
+
toHash.copy(buf, offset);
|
|
90
|
+
offset += 32;
|
|
91
|
+
const amountBuf = Buffer.alloc(16);
|
|
92
|
+
let amt = amount;
|
|
93
|
+
for (let i = 15; i >= 0; i--) {
|
|
94
|
+
amountBuf[i] = Number(amt & 0xffn);
|
|
95
|
+
amt >>= 8n;
|
|
96
|
+
}
|
|
97
|
+
amountBuf.copy(buf, offset);
|
|
98
|
+
offset += 16;
|
|
99
|
+
const timeBuf = Buffer.alloc(8);
|
|
100
|
+
let ts = BigInt(validBefore);
|
|
101
|
+
for (let i = 7; i >= 0; i--) {
|
|
102
|
+
timeBuf[i] = Number(ts & 0xffn);
|
|
103
|
+
ts >>= 8n;
|
|
104
|
+
}
|
|
105
|
+
timeBuf.copy(buf, offset);
|
|
106
|
+
offset += 8;
|
|
107
|
+
Buffer.from(nonce, "hex").copy(buf, offset);
|
|
108
|
+
const hash = createHash("sha256").update(buf).digest();
|
|
109
|
+
const sig = nacl.sign.detached(new Uint8Array(hash), kp.secretKey);
|
|
110
|
+
return toHex(sig);
|
|
111
|
+
}
|
|
112
|
+
// ── Helpers ──
|
|
113
|
+
async function teeRequest(path, options) {
|
|
114
|
+
const res = await fetch(`${TEE_URL}${path}`, options);
|
|
115
|
+
return res.json();
|
|
116
|
+
}
|
|
117
|
+
function adminHeaders() {
|
|
118
|
+
const h = { "Content-Type": "application/json" };
|
|
119
|
+
if (ADMIN_TOKEN)
|
|
120
|
+
h["Authorization"] = `Bearer ${ADMIN_TOKEN}`;
|
|
121
|
+
return h;
|
|
122
|
+
}
|
|
123
|
+
function randomHex(bytes) {
|
|
124
|
+
const buf = new Uint8Array(bytes);
|
|
125
|
+
crypto.getRandomValues(buf);
|
|
126
|
+
return toHex(buf);
|
|
127
|
+
}
|
|
128
|
+
// ── MCP Server ──
|
|
129
|
+
const server = new McpServer({
|
|
130
|
+
name: "nano-pay",
|
|
131
|
+
version: "0.1.0",
|
|
132
|
+
});
|
|
133
|
+
// Tool: nano_attestation
|
|
134
|
+
server.tool("nano_attestation", "Get TEE attestation report — proves the payment service runs in a secure enclave", {}, async () => {
|
|
135
|
+
const data = await teeRequest("/attestation");
|
|
136
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
137
|
+
});
|
|
138
|
+
// Tool: nano_balance
|
|
139
|
+
server.tool("nano_balance", "Check nanopay balance for an address", {
|
|
140
|
+
address: z.string().optional().describe("TON address to check. Defaults to agent's own address."),
|
|
141
|
+
}, async ({ address }) => {
|
|
142
|
+
const addr = address ?? agentAddress();
|
|
143
|
+
const data = await teeRequest(`/balance/${addr}`);
|
|
144
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
145
|
+
});
|
|
146
|
+
// Tool: nano_deposit
|
|
147
|
+
server.tool("nano_deposit", "Deposit funds into CyberNanoPay (simulated for testnet)", {
|
|
148
|
+
amount: z.string().describe("Amount in USDT units (6 decimals). e.g. '10000000' = $10"),
|
|
149
|
+
address: z.string().optional().describe("Depositor address. Defaults to agent's own address."),
|
|
150
|
+
}, async ({ amount, address }) => {
|
|
151
|
+
const addr = address ?? agentAddress();
|
|
152
|
+
const kp = getOrCreateKeypair();
|
|
153
|
+
// Register pubkey first
|
|
154
|
+
await teeRequest("/register-key", {
|
|
155
|
+
method: "POST",
|
|
156
|
+
headers: adminHeaders(),
|
|
157
|
+
body: JSON.stringify({ address: addr, publicKey: toHex(kp.publicKey) }),
|
|
158
|
+
});
|
|
159
|
+
// Simulate deposit
|
|
160
|
+
const data = await teeRequest("/simulate-deposit", {
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers: adminHeaders(),
|
|
163
|
+
body: JSON.stringify({ address: addr, amount }),
|
|
164
|
+
});
|
|
165
|
+
return { content: [{ type: "text", text: `Deposited ${Number(amount) / 1e6} USDT. Balance: ${Number(data.balance) / 1e6} USDT` }] };
|
|
166
|
+
});
|
|
167
|
+
// Tool: nano_pay
|
|
168
|
+
server.tool("nano_pay", "Make a gas-free micropayment via TEE", {
|
|
169
|
+
to: z.string().describe("Recipient address"),
|
|
170
|
+
amount: z.string().describe("Amount in USDT units (6 decimals). e.g. '1000' = $0.001"),
|
|
171
|
+
}, async ({ to, amount }) => {
|
|
172
|
+
const from = agentAddress();
|
|
173
|
+
const nonce = randomHex(32);
|
|
174
|
+
const validBefore = Math.floor(Date.now() / 1000) + 300;
|
|
175
|
+
const signature = signPayment(to, BigInt(amount), nonce, validBefore);
|
|
176
|
+
const data = await teeRequest("/verify", {
|
|
177
|
+
method: "POST",
|
|
178
|
+
headers: { "Content-Type": "application/json" },
|
|
179
|
+
body: JSON.stringify({ from, to, amount, validBefore, nonce, signature }),
|
|
180
|
+
});
|
|
181
|
+
if (data.success) {
|
|
182
|
+
return { content: [{ type: "text", text: `✓ Paid ${Number(amount) / 1e6} USDT to ${to}\nConfirmation: ${data.confirmationId}\nRemaining: ${Number(data.remainingBalance) / 1e6} USDT` }] };
|
|
183
|
+
}
|
|
184
|
+
return { content: [{ type: "text", text: `✗ Payment failed: ${data.error}` }] };
|
|
185
|
+
});
|
|
186
|
+
// Tool: nano_history
|
|
187
|
+
server.tool("nano_history", "View recent payment history", {
|
|
188
|
+
address: z.string().optional().describe("Address to check. Defaults to agent's own."),
|
|
189
|
+
limit: z.number().optional().describe("Max results (default 10)"),
|
|
190
|
+
}, async ({ address, limit }) => {
|
|
191
|
+
const addr = address ?? agentAddress();
|
|
192
|
+
const data = await teeRequest(`/history/payments/${addr}?limit=${limit ?? 10}`);
|
|
193
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
194
|
+
});
|
|
195
|
+
// Tool: nano_receipt
|
|
196
|
+
server.tool("nano_receipt", "Get a TEE-signed payment receipt", {
|
|
197
|
+
confirmationId: z.string().describe("Payment confirmation ID"),
|
|
198
|
+
}, async ({ confirmationId }) => {
|
|
199
|
+
const data = await teeRequest(`/receipt/${confirmationId}`);
|
|
200
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
201
|
+
});
|
|
202
|
+
// Tool: nano_stats
|
|
203
|
+
server.tool("nano_stats", "Get global protocol statistics", {}, async () => {
|
|
204
|
+
const data = await teeRequest("/stats");
|
|
205
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
206
|
+
});
|
|
207
|
+
// Tool: nano_flush
|
|
208
|
+
server.tool("nano_flush", "Trigger batch settlement — settles pending offchain payments on-chain. Use before withdrawing.", {}, async () => {
|
|
209
|
+
const data = await teeRequest("/flush", {
|
|
210
|
+
method: "POST",
|
|
211
|
+
headers: adminHeaders(),
|
|
212
|
+
});
|
|
213
|
+
if (data.settled) {
|
|
214
|
+
return { content: [{ type: "text", text: "✓ Batch settled on-chain successfully" }] };
|
|
215
|
+
}
|
|
216
|
+
return { content: [{ type: "text", text: `Flush result: ${JSON.stringify(data)}` }] };
|
|
217
|
+
});
|
|
218
|
+
// Tool: nano_withdraw
|
|
219
|
+
server.tool("nano_withdraw", "Request withdrawal — flushes unsettled payments for your address so funds can be withdrawn on-chain", {
|
|
220
|
+
address: z.string().optional().describe("Address to withdraw for. Defaults to agent's own."),
|
|
221
|
+
}, async ({ address }) => {
|
|
222
|
+
const addr = address ?? agentAddress();
|
|
223
|
+
const data = await teeRequest("/flush-for-withdraw", {
|
|
224
|
+
method: "POST",
|
|
225
|
+
headers: { "Content-Type": "application/json" },
|
|
226
|
+
body: JSON.stringify({ address: addr }),
|
|
227
|
+
});
|
|
228
|
+
if (data.error) {
|
|
229
|
+
return { content: [{ type: "text", text: `✗ Withdraw failed: ${data.error}` }] };
|
|
230
|
+
}
|
|
231
|
+
const balanceUsdt = Number(data.balance) / 1e6;
|
|
232
|
+
if (data.settled) {
|
|
233
|
+
return { content: [{ type: "text", text: `✓ Settlement complete. On-chain balance: ${balanceUsdt} USDT. You can now withdraw via the CyberGateway contract (InitiateWithdraw → wait cooldown → CompleteWithdraw).` }] };
|
|
234
|
+
}
|
|
235
|
+
return { content: [{ type: "text", text: `No pending payments to settle. Current balance: ${balanceUsdt} USDT. ${data.message ?? ""}` }] };
|
|
236
|
+
});
|
|
237
|
+
// Tool: nano_policy_get
|
|
238
|
+
server.tool("nano_policy_get", "Get spending policy for an address (limits, daily cap, HITL threshold)", {
|
|
239
|
+
address: z.string().optional().describe("Address to check. Defaults to agent's own."),
|
|
240
|
+
}, async ({ address }) => {
|
|
241
|
+
const addr = address ?? agentAddress();
|
|
242
|
+
const data = await teeRequest(`/policy/${addr}`);
|
|
243
|
+
if (!data.policy) {
|
|
244
|
+
return { content: [{ type: "text", text: `No spending policy set for ${addr}` }] };
|
|
245
|
+
}
|
|
246
|
+
const p = data.policy;
|
|
247
|
+
return { content: [{ type: "text", text: `Policy for ${addr}:\n Spending Limit: ${Number(p.spendingLimit) / 1e6} USDT per tx\n Daily Cap: ${Number(p.dailyCap) / 1e6} USDT/day\n HITL Threshold: ${Number(p.hitlThreshold) / 1e6} USDT (requires human approval above this)` }] };
|
|
248
|
+
});
|
|
249
|
+
// Tool: nano_policy_set
|
|
250
|
+
server.tool("nano_policy_set", "Set spending policy — per-transaction limit, daily cap, and HITL approval threshold", {
|
|
251
|
+
address: z.string().optional().describe("Address to set policy for. Defaults to agent's own."),
|
|
252
|
+
spendingLimit: z.string().describe("Max amount per transaction in USDT units (6 decimals). e.g. '5000000' = $5"),
|
|
253
|
+
dailyCap: z.string().describe("Max daily spending in USDT units. e.g. '50000000' = $50"),
|
|
254
|
+
hitlThreshold: z.string().describe("Payments above this require human approval. e.g. '1000000' = $1"),
|
|
255
|
+
}, async ({ address, spendingLimit, dailyCap, hitlThreshold }) => {
|
|
256
|
+
const addr = address ?? agentAddress();
|
|
257
|
+
const data = await teeRequest("/policy", {
|
|
258
|
+
method: "POST",
|
|
259
|
+
headers: adminHeaders(),
|
|
260
|
+
body: JSON.stringify({ address: addr, spendingLimit, dailyCap, hitlThreshold }),
|
|
261
|
+
});
|
|
262
|
+
if (data.success) {
|
|
263
|
+
return { content: [{ type: "text", text: `✓ Policy set for ${addr}:\n Spending Limit: ${Number(spendingLimit) / 1e6} USDT/tx\n Daily Cap: ${Number(dailyCap) / 1e6} USDT/day\n HITL Threshold: ${Number(hitlThreshold) / 1e6} USDT` }] };
|
|
264
|
+
}
|
|
265
|
+
return { content: [{ type: "text", text: `✗ Failed: ${data.error}` }] };
|
|
266
|
+
});
|
|
267
|
+
// ── Start ──
|
|
268
|
+
async function main() {
|
|
269
|
+
const transport = new StdioServerTransport();
|
|
270
|
+
await server.connect(transport);
|
|
271
|
+
}
|
|
272
|
+
main().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cyberpay/nano-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for CyberNanoPay — gas-free nanopayments on TON for AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": ["dist"],
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/CyberpayOrg/CyberNanoPay",
|
|
11
|
+
"directory": "mcp"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["mcp", "ton", "micropayments", "nanopay", "ai-agent", "claude", "cursor"],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"bin": {
|
|
18
|
+
"nano-mcp": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"dev": "tsx src/index.ts"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
26
|
+
"tweetnacl": "^1.0.3",
|
|
27
|
+
"zod": "^3.22.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22.0.0",
|
|
31
|
+
"tsx": "^4.19.0",
|
|
32
|
+
"typescript": "^5"
|
|
33
|
+
}
|
|
34
|
+
}
|