@arcisprotocol/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 +89 -0
- package/dist/index.js +394 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# @arcis/mcp
|
|
2
|
+
|
|
3
|
+
Arcis Protocol MCP Server — connect any AI agent to DeFi vaults in one tool call.
|
|
4
|
+
|
|
5
|
+
## What This Does
|
|
6
|
+
|
|
7
|
+
Any LLM that supports MCP (Claude, GPT, etc.) can interact with Arcis Protocol's on-chain contracts through natural language. No SDK, no code, no ABI decoding. The agent says "deposit 1000 USDC into Arcis" and the MCP tool handles everything.
|
|
8
|
+
|
|
9
|
+
## Tools
|
|
10
|
+
|
|
11
|
+
### Read (no auth required)
|
|
12
|
+
|
|
13
|
+
| Tool | Description |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `arcis_vault_status` | TVL, exchange rate, supply, capacity, reserve/deployed |
|
|
16
|
+
| `arcis_vault_balance` | Agent position: shares, value, USDC wallet |
|
|
17
|
+
| `arcis_preview_deposit` | Preview shares received for a given deposit |
|
|
18
|
+
| `arcis_credit_status` | Lending pool, total borrowed, utilization |
|
|
19
|
+
| `arcis_credit_tiers` | ERC-8004 reputation tier table |
|
|
20
|
+
| `arcis_credit_health` | Check if a loan is healthy |
|
|
21
|
+
| `arcis_contracts` | All 7 deployed contract addresses |
|
|
22
|
+
|
|
23
|
+
### Write (private key required)
|
|
24
|
+
|
|
25
|
+
| Tool | Description |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `arcis_deposit` | Deposit USDC → receive raUSDC (auto-approves) |
|
|
28
|
+
| `arcis_withdraw` | Redeem raUSDC → receive USDC (supports withdraw_all) |
|
|
29
|
+
|
|
30
|
+
### Resources
|
|
31
|
+
|
|
32
|
+
| URI | Description |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `arcis://protocol-info` | Protocol overview, ATI spec, product descriptions |
|
|
35
|
+
|
|
36
|
+
## Setup
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/Arcis-Protocol/mcp.git
|
|
40
|
+
cd mcp && npm install
|
|
41
|
+
npm run dev
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Connect to Claude
|
|
45
|
+
|
|
46
|
+
Add to your Claude desktop config (`~/.claude/claude_desktop_config.json`):
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"arcis": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["tsx", "/path/to/arcis-mcp/src/index.ts"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Example Interactions
|
|
60
|
+
|
|
61
|
+
> "What's the TVL on Arcis?"
|
|
62
|
+
> → Calls `arcis_vault_status`, returns $11,250.00
|
|
63
|
+
|
|
64
|
+
> "Check agent 0xB390...57db's position"
|
|
65
|
+
> → Calls `arcis_vault_balance`, returns shares + value + wallet
|
|
66
|
+
|
|
67
|
+
> "Deposit 500 USDC"
|
|
68
|
+
> → Calls `arcis_deposit`, handles approval, returns tx hash + explorer link
|
|
69
|
+
|
|
70
|
+
> "What are the credit tiers?"
|
|
71
|
+
> → Calls `arcis_credit_tiers`, returns 5-tier table with collateral ratios
|
|
72
|
+
|
|
73
|
+
## Network
|
|
74
|
+
|
|
75
|
+
Currently targets Base Sepolia testnet. Mainnet addresses will be updated after deployment.
|
|
76
|
+
|
|
77
|
+
## Related Repos
|
|
78
|
+
|
|
79
|
+
| Repo | Description |
|
|
80
|
+
|---|---|
|
|
81
|
+
| [`core`](https://github.com/Arcis-Protocol/core) | Smart contracts (Foundry) |
|
|
82
|
+
| [`sdk`](https://github.com/Arcis-Protocol/sdk) | TypeScript SDK |
|
|
83
|
+
| [`cli`](https://github.com/Arcis-Protocol/cli) | Terminal interface (TUI) |
|
|
84
|
+
| [`app`](https://github.com/Arcis-Protocol/app) | arcis.money |
|
|
85
|
+
| [`docs`](https://github.com/Arcis-Protocol/docs) | Protocol documentation |
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
*ARCIS · Of the Citadel · MMXXVI*
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { MCPServer, object, error, markdown } from "mcp-use/server";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { createPublicClient, createWalletClient, http, defineChain, formatUnits } from "viem";
|
|
7
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
8
|
+
var baseSepolia = defineChain({
|
|
9
|
+
id: 84532,
|
|
10
|
+
name: "Base Sepolia",
|
|
11
|
+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
|
12
|
+
rpcUrls: { default: { http: ["https://sepolia.base.org"] } },
|
|
13
|
+
blockExplorers: { default: { name: "Blockscout", url: "https://base-sepolia.blockscout.com" } }
|
|
14
|
+
});
|
|
15
|
+
var ADDR = {
|
|
16
|
+
vault: "0xa8eF658E125C7f6D7aFa9B6b8035b66b32CBE98d",
|
|
17
|
+
credit: "0x019540E33a0292a9DDE36bD9Ef11774d5A1Ce6FC",
|
|
18
|
+
router: "0x0281e7D37683c585325004F84e0b94170c78d5B4",
|
|
19
|
+
usdc: "0x29440A12f15fe6bDf5F624f4eeEB298CCb782f05",
|
|
20
|
+
allocator: "0x9f101e1159AA530dC5Cb104decB32aBA1eAF2617",
|
|
21
|
+
strategy: "0x9d6FB397224141FD323096e95667d3Ae5D9FF9cC",
|
|
22
|
+
identity: "0x79E79629DB86CFb8feF9594621882b065EBC80A7"
|
|
23
|
+
};
|
|
24
|
+
var VAULT_ABI = [
|
|
25
|
+
{ name: "deposit", type: "function", inputs: [{ name: "amount", type: "uint256" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "nonpayable" },
|
|
26
|
+
{ name: "withdraw", type: "function", inputs: [{ name: "shares", type: "uint256" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "nonpayable" },
|
|
27
|
+
{ name: "balance", type: "function", inputs: [{ name: "agent", type: "address" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
28
|
+
{ name: "balanceOf", type: "function", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
29
|
+
{ name: "totalAssets", type: "function", inputs: [], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
30
|
+
{ name: "totalSupply", type: "function", inputs: [], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
31
|
+
{ name: "exchangeRate", type: "function", inputs: [], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
32
|
+
{ name: "depositCap", type: "function", inputs: [], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
33
|
+
{ name: "remainingCapacity", type: "function", inputs: [], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
34
|
+
{ name: "reserveBalance", type: "function", inputs: [], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
35
|
+
{ name: "deployedBalance", type: "function", inputs: [], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
36
|
+
{ name: "paused", type: "function", inputs: [], outputs: [{ name: "", type: "bool" }], stateMutability: "view" },
|
|
37
|
+
{ name: "previewDeposit", type: "function", inputs: [{ name: "assets", type: "uint256" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
38
|
+
{ name: "previewWithdraw", type: "function", inputs: [{ name: "shares", type: "uint256" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" }
|
|
39
|
+
];
|
|
40
|
+
var CREDIT_ABI = [
|
|
41
|
+
{ name: "lendingPool", type: "function", inputs: [], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
42
|
+
{ name: "totalBorrowed", type: "function", inputs: [], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
43
|
+
{ name: "collateralRatios", type: "function", inputs: [{ name: "tier", type: "uint256" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
44
|
+
{ name: "totalOwed", type: "function", inputs: [{ name: "loanId", type: "uint256" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
45
|
+
{ name: "isHealthy", type: "function", inputs: [{ name: "loanId", type: "uint256" }], outputs: [{ name: "", type: "bool" }], stateMutability: "view" }
|
|
46
|
+
];
|
|
47
|
+
var ERC20_ABI = [
|
|
48
|
+
{ name: "approve", type: "function", inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ name: "", type: "bool" }], stateMutability: "nonpayable" },
|
|
49
|
+
{ name: "balanceOf", type: "function", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
50
|
+
{ name: "allowance", type: "function", inputs: [{ name: "owner", type: "address" }, { name: "spender", type: "address" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" }
|
|
51
|
+
];
|
|
52
|
+
var client = createPublicClient({ chain: baseSepolia, transport: http() });
|
|
53
|
+
var fmtUSDC = (raw) => formatUnits(raw, 6);
|
|
54
|
+
var fmtRate = (raw) => {
|
|
55
|
+
const n = Number(raw) / 1e18;
|
|
56
|
+
return n > 1e3 || n < 1e-4 ? "1.000000" : n.toFixed(6);
|
|
57
|
+
};
|
|
58
|
+
var server = new MCPServer({
|
|
59
|
+
name: "arcis-protocol",
|
|
60
|
+
title: "Arcis Protocol",
|
|
61
|
+
description: "Financial infrastructure for autonomous AI agents \u2014 yield vaults, credit, and bonds on Base",
|
|
62
|
+
version: "0.1.0"
|
|
63
|
+
});
|
|
64
|
+
server.tool(
|
|
65
|
+
{
|
|
66
|
+
name: "arcis_vault_status",
|
|
67
|
+
description: "Get the current vault status: TVL, exchange rate, raUSDC supply, deposit cap, reserve/deployed split, and pause state. No parameters needed.",
|
|
68
|
+
schema: z.object({})
|
|
69
|
+
},
|
|
70
|
+
async () => {
|
|
71
|
+
try {
|
|
72
|
+
const [totalAssets, totalSupply, rate, cap, remaining, reserve, deployed, paused] = await Promise.all([
|
|
73
|
+
client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "totalAssets" }),
|
|
74
|
+
client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "totalSupply" }),
|
|
75
|
+
client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "exchangeRate" }),
|
|
76
|
+
client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "depositCap" }),
|
|
77
|
+
client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "remainingCapacity" }),
|
|
78
|
+
client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "reserveBalance" }),
|
|
79
|
+
client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "deployedBalance" }),
|
|
80
|
+
client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "paused" })
|
|
81
|
+
]);
|
|
82
|
+
const utilPct = cap > 0n ? (Number(totalAssets * 10000n / cap) / 100).toFixed(2) : "0";
|
|
83
|
+
const resPct = totalAssets > 0n ? (Number(reserve * 10000n / totalAssets) / 100).toFixed(1) : "0";
|
|
84
|
+
return object({
|
|
85
|
+
network: "Base Sepolia (84532)",
|
|
86
|
+
contract: ADDR.vault,
|
|
87
|
+
status: paused ? "PAUSED" : "ACTIVE",
|
|
88
|
+
tvl_usdc: fmtUSDC(totalAssets),
|
|
89
|
+
rausdc_supply: fmtUSDC(totalSupply),
|
|
90
|
+
exchange_rate: totalSupply > 0n ? fmtRate(rate) : "1.000000",
|
|
91
|
+
reserve_usdc: fmtUSDC(reserve),
|
|
92
|
+
reserve_pct: resPct + "%",
|
|
93
|
+
deployed_usdc: fmtUSDC(deployed),
|
|
94
|
+
deposit_cap_usdc: cap > 0n ? fmtUSDC(cap) : "No cap set",
|
|
95
|
+
remaining_capacity: cap > 0n ? fmtUSDC(remaining) : "Unlimited",
|
|
96
|
+
utilization: utilPct + "%",
|
|
97
|
+
explorer: `https://base-sepolia.blockscout.com/address/${ADDR.vault}`
|
|
98
|
+
});
|
|
99
|
+
} catch (e) {
|
|
100
|
+
return error("Failed to fetch vault status: " + e.message);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
server.tool(
|
|
105
|
+
{
|
|
106
|
+
name: "arcis_vault_balance",
|
|
107
|
+
description: "Check an agent's vault position: raUSDC shares held, position value in USDC, and USDC wallet balance.",
|
|
108
|
+
schema: z.object({
|
|
109
|
+
address: z.string().describe("The agent's Ethereum address (0x...)")
|
|
110
|
+
})
|
|
111
|
+
},
|
|
112
|
+
async ({ address }) => {
|
|
113
|
+
try {
|
|
114
|
+
const agent = address;
|
|
115
|
+
const [shares, value, usdcBal] = await Promise.all([
|
|
116
|
+
client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "balanceOf", args: [agent] }),
|
|
117
|
+
client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "balance", args: [agent] }),
|
|
118
|
+
client.readContract({ address: ADDR.usdc, abi: ERC20_ABI, functionName: "balanceOf", args: [agent] })
|
|
119
|
+
]);
|
|
120
|
+
return object({
|
|
121
|
+
agent: address,
|
|
122
|
+
rausdc_shares: fmtUSDC(shares),
|
|
123
|
+
position_value_usdc: fmtUSDC(value),
|
|
124
|
+
usdc_wallet_balance: fmtUSDC(usdcBal)
|
|
125
|
+
});
|
|
126
|
+
} catch (e) {
|
|
127
|
+
return error("Failed to fetch balance: " + e.message);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
server.tool(
|
|
132
|
+
{
|
|
133
|
+
name: "arcis_preview_deposit",
|
|
134
|
+
description: "Preview how many raUSDC shares a given USDC deposit would yield, without executing the transaction.",
|
|
135
|
+
schema: z.object({
|
|
136
|
+
amount: z.number().positive().describe("Amount of USDC to deposit (e.g. 1000 for $1,000)")
|
|
137
|
+
})
|
|
138
|
+
},
|
|
139
|
+
async ({ amount }) => {
|
|
140
|
+
try {
|
|
141
|
+
const raw = BigInt(Math.floor(amount * 1e6));
|
|
142
|
+
const shares = await client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "previewDeposit", args: [raw] });
|
|
143
|
+
return object({
|
|
144
|
+
deposit_usdc: amount.toString(),
|
|
145
|
+
shares_received: fmtUSDC(shares),
|
|
146
|
+
exchange_rate: (amount * 1e6 / Number(shares)).toFixed(6)
|
|
147
|
+
});
|
|
148
|
+
} catch (e) {
|
|
149
|
+
return error("Preview failed: " + e.message);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
server.tool(
|
|
154
|
+
{
|
|
155
|
+
name: "arcis_credit_status",
|
|
156
|
+
description: "Get the credit module status: lending pool size, total borrowed, and utilization rate.",
|
|
157
|
+
schema: z.object({})
|
|
158
|
+
},
|
|
159
|
+
async () => {
|
|
160
|
+
try {
|
|
161
|
+
const [pool, borrowed] = await Promise.all([
|
|
162
|
+
client.readContract({ address: ADDR.credit, abi: CREDIT_ABI, functionName: "lendingPool" }),
|
|
163
|
+
client.readContract({ address: ADDR.credit, abi: CREDIT_ABI, functionName: "totalBorrowed" })
|
|
164
|
+
]);
|
|
165
|
+
const total = pool + borrowed;
|
|
166
|
+
const utilPct = total > 0n ? (Number(borrowed * 10000n / total) / 100).toFixed(1) : "0";
|
|
167
|
+
return object({
|
|
168
|
+
contract: ADDR.credit,
|
|
169
|
+
lending_pool_usdc: fmtUSDC(pool),
|
|
170
|
+
total_borrowed_usdc: fmtUSDC(borrowed),
|
|
171
|
+
utilization: utilPct + "%"
|
|
172
|
+
});
|
|
173
|
+
} catch (e) {
|
|
174
|
+
return error("Failed to fetch credit status: " + e.message);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
server.tool(
|
|
179
|
+
{
|
|
180
|
+
name: "arcis_credit_tiers",
|
|
181
|
+
description: "List all 5 ERC-8004 reputation tiers with their collateral ratios and rate discounts.",
|
|
182
|
+
schema: z.object({})
|
|
183
|
+
},
|
|
184
|
+
async () => {
|
|
185
|
+
try {
|
|
186
|
+
const labels = ["No Identity", "Novice (1-25)", "Active (26-50)", "Established (51-75)", "Elite (76-100)"];
|
|
187
|
+
const discounts = [0, 100, 200, 350, 500];
|
|
188
|
+
const ratios = await Promise.all(
|
|
189
|
+
[0, 1, 2, 3, 4].map(
|
|
190
|
+
(i) => client.readContract({ address: ADDR.credit, abi: CREDIT_ABI, functionName: "collateralRatios", args: [BigInt(i)] })
|
|
191
|
+
)
|
|
192
|
+
);
|
|
193
|
+
return object({
|
|
194
|
+
tiers: ratios.map((r, i) => ({
|
|
195
|
+
tier: i,
|
|
196
|
+
label: labels[i],
|
|
197
|
+
collateral_ratio: (Number(r) / 100).toFixed(1) + "%",
|
|
198
|
+
rate_discount_bps: discounts[i]
|
|
199
|
+
}))
|
|
200
|
+
});
|
|
201
|
+
} catch (e) {
|
|
202
|
+
return error("Failed to fetch tiers: " + e.message);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
server.tool(
|
|
207
|
+
{
|
|
208
|
+
name: "arcis_credit_health",
|
|
209
|
+
description: "Check whether a specific loan is healthy (sufficiently collateralized) and how much is owed.",
|
|
210
|
+
schema: z.object({
|
|
211
|
+
loan_id: z.number().int().positive().describe("The loan ID to check")
|
|
212
|
+
})
|
|
213
|
+
},
|
|
214
|
+
async ({ loan_id }) => {
|
|
215
|
+
try {
|
|
216
|
+
const [healthy, owed] = await Promise.all([
|
|
217
|
+
client.readContract({ address: ADDR.credit, abi: CREDIT_ABI, functionName: "isHealthy", args: [BigInt(loan_id)] }),
|
|
218
|
+
client.readContract({ address: ADDR.credit, abi: CREDIT_ABI, functionName: "totalOwed", args: [BigInt(loan_id)] })
|
|
219
|
+
]);
|
|
220
|
+
return object({
|
|
221
|
+
loan_id,
|
|
222
|
+
healthy,
|
|
223
|
+
total_owed_usdc: fmtUSDC(owed),
|
|
224
|
+
status: healthy ? "HEALTHY" : "UNDERCOLLATERALIZED"
|
|
225
|
+
});
|
|
226
|
+
} catch (e) {
|
|
227
|
+
return error("Failed to check loan health: " + e.message);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
server.tool(
|
|
232
|
+
{
|
|
233
|
+
name: "arcis_contracts",
|
|
234
|
+
description: "List all deployed Arcis Protocol contract addresses on Base Sepolia with their roles.",
|
|
235
|
+
schema: z.object({})
|
|
236
|
+
},
|
|
237
|
+
async () => {
|
|
238
|
+
return object({
|
|
239
|
+
network: "Base Sepolia (84532)",
|
|
240
|
+
explorer: "https://base-sepolia.blockscout.com",
|
|
241
|
+
contracts: {
|
|
242
|
+
ArcisVault: { address: ADDR.vault, role: "ERC-4626 yield vault (raUSDC)" },
|
|
243
|
+
AgentCredit: { address: ADDR.credit, role: "ERC-8004 identity-aware lending" },
|
|
244
|
+
ATIRouter: { address: ADDR.router, role: "Multi-vault entry point" },
|
|
245
|
+
StrategyAllocator: { address: ADDR.allocator, role: "Yield strategy weights" },
|
|
246
|
+
MockStrategy: { address: ADDR.strategy, role: "Testnet yield strategy" },
|
|
247
|
+
MockUSDC: { address: ADDR.usdc, role: "Testnet USDC" },
|
|
248
|
+
MockIdentityRegistry: { address: ADDR.identity, role: "ERC-8004 test registry" }
|
|
249
|
+
},
|
|
250
|
+
ati_standard: {
|
|
251
|
+
deposit: "deposit(uint256 amount) \u2192 uint256 shares",
|
|
252
|
+
withdraw: "withdraw(uint256 shares) \u2192 uint256 amount",
|
|
253
|
+
balance: "balance(address agent) \u2192 uint256 value"
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
);
|
|
258
|
+
server.tool(
|
|
259
|
+
{
|
|
260
|
+
name: "arcis_deposit",
|
|
261
|
+
description: "Deposit USDC into the Arcis vault and receive raUSDC shares. Handles USDC approval automatically. Requires an agent private key.",
|
|
262
|
+
schema: z.object({
|
|
263
|
+
amount: z.number().positive().describe("Amount of USDC to deposit (e.g. 1000 for $1,000)"),
|
|
264
|
+
private_key: z.string().describe("Agent's private key (0x...) for signing the transaction")
|
|
265
|
+
})
|
|
266
|
+
},
|
|
267
|
+
async ({ amount, private_key }) => {
|
|
268
|
+
try {
|
|
269
|
+
const amountRaw = BigInt(Math.floor(amount * 1e6));
|
|
270
|
+
const account = privateKeyToAccount(private_key);
|
|
271
|
+
const wallet = createWalletClient({ chain: baseSepolia, transport: http(), account });
|
|
272
|
+
const usdcBal = await client.readContract({ address: ADDR.usdc, abi: ERC20_ABI, functionName: "balanceOf", args: [account.address] });
|
|
273
|
+
if (usdcBal < amountRaw) {
|
|
274
|
+
return error(`Insufficient USDC. Have ${fmtUSDC(usdcBal)}, need ${fmtUSDC(amountRaw)}`);
|
|
275
|
+
}
|
|
276
|
+
const allowance = await client.readContract({ address: ADDR.usdc, abi: ERC20_ABI, functionName: "allowance", args: [account.address, ADDR.vault] });
|
|
277
|
+
if (allowance < amountRaw) {
|
|
278
|
+
const approveTx = await wallet.writeContract({
|
|
279
|
+
address: ADDR.usdc,
|
|
280
|
+
abi: ERC20_ABI,
|
|
281
|
+
functionName: "approve",
|
|
282
|
+
args: [ADDR.vault, 2n ** 256n - 1n],
|
|
283
|
+
chain: baseSepolia,
|
|
284
|
+
account
|
|
285
|
+
});
|
|
286
|
+
await client.waitForTransactionReceipt({ hash: approveTx });
|
|
287
|
+
}
|
|
288
|
+
const sharesPreview = await client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "previewDeposit", args: [amountRaw] });
|
|
289
|
+
const tx = await wallet.writeContract({
|
|
290
|
+
address: ADDR.vault,
|
|
291
|
+
abi: VAULT_ABI,
|
|
292
|
+
functionName: "deposit",
|
|
293
|
+
args: [amountRaw],
|
|
294
|
+
chain: baseSepolia,
|
|
295
|
+
account
|
|
296
|
+
});
|
|
297
|
+
const receipt = await client.waitForTransactionReceipt({ hash: tx });
|
|
298
|
+
return object({
|
|
299
|
+
status: "SUCCESS",
|
|
300
|
+
deposited_usdc: amount.toString(),
|
|
301
|
+
shares_received: fmtUSDC(sharesPreview),
|
|
302
|
+
tx_hash: tx,
|
|
303
|
+
block: Number(receipt.blockNumber),
|
|
304
|
+
gas_used: Number(receipt.gasUsed),
|
|
305
|
+
explorer: `https://base-sepolia.blockscout.com/tx/${tx}`
|
|
306
|
+
});
|
|
307
|
+
} catch (e) {
|
|
308
|
+
return error("Deposit failed: " + e.message);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
server.tool(
|
|
313
|
+
{
|
|
314
|
+
name: "arcis_withdraw",
|
|
315
|
+
description: "Withdraw USDC from the Arcis vault by redeeming raUSDC shares. Use withdraw_all to redeem entire position.",
|
|
316
|
+
schema: z.object({
|
|
317
|
+
shares: z.number().positive().optional().describe("Amount of raUSDC shares to redeem (omit to withdraw all)"),
|
|
318
|
+
private_key: z.string().describe("Agent's private key (0x...) for signing the transaction"),
|
|
319
|
+
withdraw_all: z.boolean().optional().describe("Set to true to withdraw entire position")
|
|
320
|
+
})
|
|
321
|
+
},
|
|
322
|
+
async ({ shares, private_key, withdraw_all }) => {
|
|
323
|
+
try {
|
|
324
|
+
const account = privateKeyToAccount(private_key);
|
|
325
|
+
const wallet = createWalletClient({ chain: baseSepolia, transport: http(), account });
|
|
326
|
+
let sharesRaw;
|
|
327
|
+
if (withdraw_all) {
|
|
328
|
+
sharesRaw = await client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "balanceOf", args: [account.address] });
|
|
329
|
+
if (sharesRaw === 0n) return error("No shares to withdraw");
|
|
330
|
+
} else if (shares) {
|
|
331
|
+
sharesRaw = BigInt(Math.floor(shares * 1e6));
|
|
332
|
+
} else {
|
|
333
|
+
return error("Provide either shares amount or set withdraw_all to true");
|
|
334
|
+
}
|
|
335
|
+
const usdcPreview = await client.readContract({ address: ADDR.vault, abi: VAULT_ABI, functionName: "previewWithdraw", args: [sharesRaw] });
|
|
336
|
+
const tx = await wallet.writeContract({
|
|
337
|
+
address: ADDR.vault,
|
|
338
|
+
abi: VAULT_ABI,
|
|
339
|
+
functionName: "withdraw",
|
|
340
|
+
args: [sharesRaw],
|
|
341
|
+
chain: baseSepolia,
|
|
342
|
+
account
|
|
343
|
+
});
|
|
344
|
+
const receipt = await client.waitForTransactionReceipt({ hash: tx });
|
|
345
|
+
return object({
|
|
346
|
+
status: "SUCCESS",
|
|
347
|
+
shares_redeemed: fmtUSDC(sharesRaw),
|
|
348
|
+
usdc_received: fmtUSDC(usdcPreview),
|
|
349
|
+
tx_hash: tx,
|
|
350
|
+
block: Number(receipt.blockNumber),
|
|
351
|
+
explorer: `https://base-sepolia.blockscout.com/tx/${tx}`
|
|
352
|
+
});
|
|
353
|
+
} catch (e) {
|
|
354
|
+
return error("Withdraw failed: " + e.message);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
);
|
|
358
|
+
server.resource(
|
|
359
|
+
{
|
|
360
|
+
uri: "arcis://protocol-info",
|
|
361
|
+
name: "Arcis Protocol Info",
|
|
362
|
+
description: "Overview of Arcis Protocol \u2014 what it is, how it works, and how agents interact with it",
|
|
363
|
+
mimeType: "text/markdown"
|
|
364
|
+
},
|
|
365
|
+
async () => markdown(`# Arcis Protocol
|
|
366
|
+
|
|
367
|
+
**The citadel of agent capital.**
|
|
368
|
+
|
|
369
|
+
Arcis builds financial infrastructure for autonomous AI agents: yield-bearing vaults, identity-aware credit lines, and revenue bonds. Deployed on Base.
|
|
370
|
+
|
|
371
|
+
## ATI Standard (Agent Treasury Interface)
|
|
372
|
+
|
|
373
|
+
Three functions. Any agent framework.
|
|
374
|
+
|
|
375
|
+
- \`deposit(uint256 amount) \u2192 uint256 shares\` \u2014 Deposit USDC, receive yield-bearing raUSDC
|
|
376
|
+
- \`withdraw(uint256 shares) \u2192 uint256 amount\` \u2014 Redeem raUSDC for USDC + accrued yield
|
|
377
|
+
- \`balance(address agent) \u2192 uint256 value\` \u2014 Check position value in USDC
|
|
378
|
+
|
|
379
|
+
## Products
|
|
380
|
+
|
|
381
|
+
1. **Agent Vaults** \u2014 ERC-4626 vaults where agents park idle USDC. Yield via Aave/Morpho strategies. Management fee: 2% on yield.
|
|
382
|
+
2. **Agent Credit** \u2014 ERC-8004 reputation-aware lending. 5 tiers from 200% to 115% collateral based on on-chain identity score.
|
|
383
|
+
3. **Revenue Bonds** \u2014 Agents with consistent revenue issue tokenized bonds. Human investors buy fixed yield.
|
|
384
|
+
|
|
385
|
+
## Links
|
|
386
|
+
|
|
387
|
+
- Website: https://arcis.money
|
|
388
|
+
- Dashboard: https://arcis.money/dashboard
|
|
389
|
+
- GitHub: https://github.com/Arcis-Protocol
|
|
390
|
+
- Telegram: https://t.me/arcisprotocol
|
|
391
|
+
- X: https://x.com/ArcisProtocol
|
|
392
|
+
`)
|
|
393
|
+
);
|
|
394
|
+
server.listen();
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@arcisprotocol/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Arcis Protocol MCP Server — connect any AI agent to DeFi vaults in one tool call",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"arcis-mcp": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup src/index.ts --format esm --clean",
|
|
16
|
+
"dev": "tsx src/index.ts",
|
|
17
|
+
"start": "node dist/index.js",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"keywords": ["arcis", "mcp", "model-context-protocol", "defi", "ai-agents", "vault", "base"],
|
|
21
|
+
"author": "Arcis Protocol <dev@arcis.money>",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/Arcis-Protocol/mcp.git"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://arcis.money",
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"mcp-use": "^0.14.0",
|
|
33
|
+
"viem": "^2.21.0",
|
|
34
|
+
"zod": "^3.23.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"typescript": "^5.6.0",
|
|
38
|
+
"tsup": "^8.3.0",
|
|
39
|
+
"tsx": "^4.19.0",
|
|
40
|
+
"@types/node": "^22.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|