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