@azeth/mcp-server 0.2.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/LICENSE +21 -0
- package/README.md +141 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/account.d.ts +4 -0
- package/dist/tools/account.d.ts.map +1 -0
- package/dist/tools/account.js +640 -0
- package/dist/tools/account.js.map +1 -0
- package/dist/tools/agreements.d.ts +4 -0
- package/dist/tools/agreements.d.ts.map +1 -0
- package/dist/tools/agreements.js +865 -0
- package/dist/tools/agreements.js.map +1 -0
- package/dist/tools/guardian-approval.d.ts +4 -0
- package/dist/tools/guardian-approval.d.ts.map +1 -0
- package/dist/tools/guardian-approval.js +319 -0
- package/dist/tools/guardian-approval.js.map +1 -0
- package/dist/tools/guardian.d.ts +4 -0
- package/dist/tools/guardian.d.ts.map +1 -0
- package/dist/tools/guardian.js +267 -0
- package/dist/tools/guardian.js.map +1 -0
- package/dist/tools/messaging.d.ts +4 -0
- package/dist/tools/messaging.d.ts.map +1 -0
- package/dist/tools/messaging.js +353 -0
- package/dist/tools/messaging.js.map +1 -0
- package/dist/tools/payments.d.ts +14 -0
- package/dist/tools/payments.d.ts.map +1 -0
- package/dist/tools/payments.js +723 -0
- package/dist/tools/payments.js.map +1 -0
- package/dist/tools/registry.d.ts +4 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +608 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/reputation.d.ts +4 -0
- package/dist/tools/reputation.d.ts.map +1 -0
- package/dist/tools/reputation.js +433 -0
- package/dist/tools/reputation.js.map +1 -0
- package/dist/tools/transfer.d.ts +4 -0
- package/dist/tools/transfer.d.ts.map +1 -0
- package/dist/tools/transfer.js +181 -0
- package/dist/tools/transfer.js.map +1 -0
- package/dist/utils/client.d.ts +25 -0
- package/dist/utils/client.d.ts.map +1 -0
- package/dist/utils/client.js +100 -0
- package/dist/utils/client.js.map +1 -0
- package/dist/utils/error-selectors.d.ts +23 -0
- package/dist/utils/error-selectors.d.ts.map +1 -0
- package/dist/utils/error-selectors.js +159 -0
- package/dist/utils/error-selectors.js.map +1 -0
- package/dist/utils/rate-limit.d.ts +17 -0
- package/dist/utils/rate-limit.d.ts.map +1 -0
- package/dist/utils/rate-limit.js +75 -0
- package/dist/utils/rate-limit.js.map +1 -0
- package/dist/utils/resolve.d.ts +38 -0
- package/dist/utils/resolve.d.ts.map +1 -0
- package/dist/utils/resolve.js +308 -0
- package/dist/utils/resolve.js.map +1 -0
- package/dist/utils/response.d.ts +42 -0
- package/dist/utils/response.d.ts.map +1 -0
- package/dist/utils/response.js +257 -0
- package/dist/utils/response.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatUnits } from 'viem';
|
|
3
|
+
import { AZETH_CONTRACTS, TOKENS } from '@azeth/common';
|
|
4
|
+
import { GuardianModuleAbi } from '@azeth/common/abis';
|
|
5
|
+
import { createClient, resolveChain, validateAddress } from '../utils/client.js';
|
|
6
|
+
import { resolveSmartAccount } from '../utils/resolve.js';
|
|
7
|
+
import { success, error, handleError, guardianRequiredError } from '../utils/response.js';
|
|
8
|
+
/** Register guardian MCP tools: azeth_get_guardrails, azeth_whitelist_protocol */
|
|
9
|
+
export function registerGuardianTools(server) {
|
|
10
|
+
// ──────────────────────────────────────────────
|
|
11
|
+
// azeth_get_guardrails
|
|
12
|
+
// ──────────────────────────────────────────────
|
|
13
|
+
server.registerTool('azeth_get_guardrails', {
|
|
14
|
+
description: [
|
|
15
|
+
'View the guardian security configuration for a smart account.',
|
|
16
|
+
'',
|
|
17
|
+
'Use this when: You want to check spending limits, token/protocol whitelists,',
|
|
18
|
+
'daily spend tracking, emergency withdrawal status, or pending guardrail changes.',
|
|
19
|
+
'',
|
|
20
|
+
'Returns: Full guardian state including spending limits (USD), whitelisted tokens',
|
|
21
|
+
'and protocols, daily spend progress, and any pending timelock changes.',
|
|
22
|
+
'',
|
|
23
|
+
'This is read-only and safe to call at any time.',
|
|
24
|
+
'',
|
|
25
|
+
'Example: { "smartAccount": "me" }',
|
|
26
|
+
].join('\n'),
|
|
27
|
+
inputSchema: z.object({
|
|
28
|
+
chain: z.string().optional().describe('Target chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet").'),
|
|
29
|
+
smartAccount: z.string().optional().describe('Smart account to inspect. Accepts address, "me", "#N", or account name. Defaults to first account.'),
|
|
30
|
+
}),
|
|
31
|
+
}, async (args) => {
|
|
32
|
+
let client;
|
|
33
|
+
try {
|
|
34
|
+
client = await createClient(args.chain);
|
|
35
|
+
// Resolve smart account (defaults to first)
|
|
36
|
+
let account;
|
|
37
|
+
if (args.smartAccount) {
|
|
38
|
+
try {
|
|
39
|
+
account = (await resolveSmartAccount(args.smartAccount, client));
|
|
40
|
+
}
|
|
41
|
+
catch (resolveErr) {
|
|
42
|
+
return handleError(resolveErr);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
account = await client.resolveSmartAccount();
|
|
47
|
+
}
|
|
48
|
+
const chain = resolveChain(args.chain);
|
|
49
|
+
const guardianAddr = AZETH_CONTRACTS[chain].guardianModule;
|
|
50
|
+
// Parallel reads for efficiency: guardrails, daily spend, pending changes
|
|
51
|
+
const [guardrails, dailySpentUSD, pendingChange, pendingEmergency] = await Promise.all([
|
|
52
|
+
client.publicClient.readContract({
|
|
53
|
+
address: guardianAddr,
|
|
54
|
+
abi: GuardianModuleAbi,
|
|
55
|
+
functionName: 'getGuardrails',
|
|
56
|
+
args: [account],
|
|
57
|
+
}),
|
|
58
|
+
client.publicClient.readContract({
|
|
59
|
+
address: guardianAddr,
|
|
60
|
+
abi: GuardianModuleAbi,
|
|
61
|
+
functionName: 'getDailySpentUSD',
|
|
62
|
+
args: [account],
|
|
63
|
+
}),
|
|
64
|
+
client.publicClient.readContract({
|
|
65
|
+
address: guardianAddr,
|
|
66
|
+
abi: GuardianModuleAbi,
|
|
67
|
+
functionName: 'getPendingChange',
|
|
68
|
+
args: [account],
|
|
69
|
+
}),
|
|
70
|
+
client.publicClient.readContract({
|
|
71
|
+
address: guardianAddr,
|
|
72
|
+
abi: GuardianModuleAbi,
|
|
73
|
+
functionName: 'getPendingEmergency',
|
|
74
|
+
args: [account],
|
|
75
|
+
}),
|
|
76
|
+
]);
|
|
77
|
+
// Check common token whitelists (ETH, USDC, WETH)
|
|
78
|
+
const ETH = '0x0000000000000000000000000000000000000000';
|
|
79
|
+
const usdc = TOKENS[chain].USDC;
|
|
80
|
+
const weth = TOKENS[chain].WETH;
|
|
81
|
+
// Check common protocol whitelists (Azeth modules)
|
|
82
|
+
const paymentAgreementModule = AZETH_CONTRACTS[chain].paymentAgreementModule;
|
|
83
|
+
const reputationModule = AZETH_CONTRACTS[chain].reputationModule;
|
|
84
|
+
const [ethWhitelisted, usdcWhitelisted, wethWhitelisted, paymentAgreementWhitelisted, reputationWhitelisted,] = await Promise.all([
|
|
85
|
+
client.publicClient.readContract({
|
|
86
|
+
address: guardianAddr,
|
|
87
|
+
abi: GuardianModuleAbi,
|
|
88
|
+
functionName: 'isTokenWhitelisted',
|
|
89
|
+
args: [account, ETH],
|
|
90
|
+
}),
|
|
91
|
+
client.publicClient.readContract({
|
|
92
|
+
address: guardianAddr,
|
|
93
|
+
abi: GuardianModuleAbi,
|
|
94
|
+
functionName: 'isTokenWhitelisted',
|
|
95
|
+
args: [account, usdc],
|
|
96
|
+
}),
|
|
97
|
+
client.publicClient.readContract({
|
|
98
|
+
address: guardianAddr,
|
|
99
|
+
abi: GuardianModuleAbi,
|
|
100
|
+
functionName: 'isTokenWhitelisted',
|
|
101
|
+
args: [account, weth],
|
|
102
|
+
}),
|
|
103
|
+
client.publicClient.readContract({
|
|
104
|
+
address: guardianAddr,
|
|
105
|
+
abi: GuardianModuleAbi,
|
|
106
|
+
functionName: 'isProtocolWhitelisted',
|
|
107
|
+
args: [account, paymentAgreementModule],
|
|
108
|
+
}),
|
|
109
|
+
client.publicClient.readContract({
|
|
110
|
+
address: guardianAddr,
|
|
111
|
+
abi: GuardianModuleAbi,
|
|
112
|
+
functionName: 'isProtocolWhitelisted',
|
|
113
|
+
args: [account, reputationModule],
|
|
114
|
+
}),
|
|
115
|
+
]);
|
|
116
|
+
// Format the Guardrails struct (6 fields from ABI)
|
|
117
|
+
const g = guardrails;
|
|
118
|
+
const dailySpent = dailySpentUSD;
|
|
119
|
+
const dailyLimit = g.dailySpendLimitUSD;
|
|
120
|
+
const dailyRemaining = dailyLimit > dailySpent ? dailyLimit - dailySpent : 0n;
|
|
121
|
+
const dailyPercentUsed = dailyLimit > 0n
|
|
122
|
+
? Number((dailySpent * 10000n) / dailyLimit) / 100
|
|
123
|
+
: 0;
|
|
124
|
+
// Format pending change
|
|
125
|
+
const pc = pendingChange;
|
|
126
|
+
const pe = pendingEmergency;
|
|
127
|
+
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
128
|
+
return success({
|
|
129
|
+
account,
|
|
130
|
+
spendingLimits: {
|
|
131
|
+
maxTransactionUSD: `$${formatUnits(g.maxTxAmountUSD, 18)}`,
|
|
132
|
+
dailySpendLimitUSD: `$${formatUnits(dailyLimit, 18)}`,
|
|
133
|
+
dailySpentUSD: `$${formatUnits(dailySpent, 18)}`,
|
|
134
|
+
dailyRemainingUSD: `$${formatUnits(dailyRemaining, 18)}`,
|
|
135
|
+
dailyPercentUsed: `${dailyPercentUsed.toFixed(1)}%`,
|
|
136
|
+
},
|
|
137
|
+
guardianLimits: {
|
|
138
|
+
guardianMaxTransactionUSD: `$${formatUnits(g.guardianMaxTxAmountUSD, 18)}`,
|
|
139
|
+
guardianDailySpendLimitUSD: `$${formatUnits(g.guardianDailySpendLimitUSD, 18)}`,
|
|
140
|
+
},
|
|
141
|
+
guardian: g.guardian,
|
|
142
|
+
emergencyWithdrawTo: g.emergencyWithdrawTo,
|
|
143
|
+
tokenWhitelist: {
|
|
144
|
+
ETH: ethWhitelisted,
|
|
145
|
+
USDC: usdcWhitelisted,
|
|
146
|
+
WETH: wethWhitelisted,
|
|
147
|
+
},
|
|
148
|
+
protocolWhitelist: {
|
|
149
|
+
PaymentAgreementModule: paymentAgreementWhitelisted,
|
|
150
|
+
ReputationModule: reputationWhitelisted,
|
|
151
|
+
},
|
|
152
|
+
pendingGuardrailChange: pc.exists ? {
|
|
153
|
+
changeHash: pc.changeHash,
|
|
154
|
+
executeAfter: new Date(Number(pc.executeAfter) * 1000).toISOString(),
|
|
155
|
+
readyIn: pc.executeAfter > now
|
|
156
|
+
? `${Math.ceil(Number(pc.executeAfter - now) / 60)} minutes`
|
|
157
|
+
: 'READY to execute',
|
|
158
|
+
} : null,
|
|
159
|
+
pendingEmergencyWithdrawal: pe.exists ? {
|
|
160
|
+
token: pe.token,
|
|
161
|
+
executeAfter: new Date(Number(pe.executeAfter) * 1000).toISOString(),
|
|
162
|
+
readyIn: pe.executeAfter > now
|
|
163
|
+
? `${Math.ceil(Number(pe.executeAfter - now) / 60)} minutes`
|
|
164
|
+
: 'READY to execute',
|
|
165
|
+
} : null,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
return handleError(err);
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
try {
|
|
173
|
+
await client?.destroy();
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
process.stderr.write(`[azeth-mcp] destroy error: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
// ──────────────────────────────────────────────
|
|
181
|
+
// azeth_whitelist_protocol
|
|
182
|
+
// ──────────────────────────────────────────────
|
|
183
|
+
server.registerTool('azeth_whitelist_protocol', {
|
|
184
|
+
description: [
|
|
185
|
+
'Add or remove a protocol (contract address) from your smart account\'s guardian whitelist.',
|
|
186
|
+
'',
|
|
187
|
+
'Use this when: You need to interact with a new DeFi protocol or contract through',
|
|
188
|
+
'executor modules (like PaymentAgreementModule). Protocols must be whitelisted for',
|
|
189
|
+
'automated operations to succeed.',
|
|
190
|
+
'',
|
|
191
|
+
'The "protocol" field must be a valid Ethereum address of the contract to whitelist.',
|
|
192
|
+
'',
|
|
193
|
+
'Returns: Confirmation of the whitelist update with transaction hash.',
|
|
194
|
+
'',
|
|
195
|
+
'Note: This requires a UserOperation (gas). Only the account owner can modify whitelists.',
|
|
196
|
+
'Whitelisting a protocol allows executor modules to interact with it on your behalf.',
|
|
197
|
+
'Whitelist additions require guardian co-signature for security.',
|
|
198
|
+
'',
|
|
199
|
+
'Example: { "protocol": "0x71D52798e3D0f5766f6f0AFEd6710EB5D1FF4DF9", "allowed": true }',
|
|
200
|
+
].join('\n'),
|
|
201
|
+
inputSchema: z.object({
|
|
202
|
+
chain: z.string().optional().describe('Target chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet").'),
|
|
203
|
+
protocol: z.string().describe('Protocol/contract address to whitelist or delist (0x...).'),
|
|
204
|
+
allowed: z.boolean().describe('true to whitelist, false to remove from whitelist.'),
|
|
205
|
+
smartAccount: z.string().optional().describe('Smart account address, name, or "#N". Defaults to first smart account.'),
|
|
206
|
+
}),
|
|
207
|
+
}, async (args) => {
|
|
208
|
+
if (!validateAddress(args.protocol)) {
|
|
209
|
+
return error('INVALID_INPUT', `Invalid protocol address: "${args.protocol}".`, 'Must be 0x-prefixed followed by 40 hex characters.');
|
|
210
|
+
}
|
|
211
|
+
let client;
|
|
212
|
+
try {
|
|
213
|
+
client = await createClient(args.chain);
|
|
214
|
+
let account;
|
|
215
|
+
if (args.smartAccount) {
|
|
216
|
+
try {
|
|
217
|
+
account = (await resolveSmartAccount(args.smartAccount, client));
|
|
218
|
+
}
|
|
219
|
+
catch (resolveErr) {
|
|
220
|
+
return handleError(resolveErr);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
account = await client.resolveSmartAccount();
|
|
225
|
+
}
|
|
226
|
+
const txHash = await client.setProtocolWhitelist(args.protocol, args.allowed, account);
|
|
227
|
+
const action = args.allowed ? 'whitelisted' : 'removed from whitelist';
|
|
228
|
+
// Resolve protocol name for known Azeth contracts
|
|
229
|
+
const chain = resolveChain(args.chain);
|
|
230
|
+
const contracts = AZETH_CONTRACTS[chain];
|
|
231
|
+
const protocolLower = args.protocol.toLowerCase();
|
|
232
|
+
let protocolName = args.protocol.slice(0, 6) + '...' + args.protocol.slice(-4);
|
|
233
|
+
if (protocolLower === contracts.paymentAgreementModule.toLowerCase())
|
|
234
|
+
protocolName = 'PaymentAgreementModule';
|
|
235
|
+
else if (protocolLower === contracts.reputationModule.toLowerCase())
|
|
236
|
+
protocolName = 'ReputationModule';
|
|
237
|
+
else if (protocolLower === contracts.trustRegistryModule.toLowerCase())
|
|
238
|
+
protocolName = 'TrustRegistryModule';
|
|
239
|
+
else if (protocolLower === contracts.guardianModule.toLowerCase())
|
|
240
|
+
protocolName = 'GuardianModule';
|
|
241
|
+
else if (protocolLower === contracts.factory.toLowerCase())
|
|
242
|
+
protocolName = 'AzethFactory';
|
|
243
|
+
return success({
|
|
244
|
+
protocol: args.protocol,
|
|
245
|
+
protocolName,
|
|
246
|
+
allowed: args.allowed,
|
|
247
|
+
message: `${protocolName} (${args.protocol}) ${action} on account ${account}.`,
|
|
248
|
+
}, { txHash });
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
// Detect AA24 signature validation failure — common for guardian-gated operations
|
|
252
|
+
if (err instanceof Error && /AA24/.test(err.message)) {
|
|
253
|
+
return guardianRequiredError('Protocol whitelisting requires guardian co-signature (guardrail-loosening change).', { operation: 'whitelist_protocol' });
|
|
254
|
+
}
|
|
255
|
+
return handleError(err);
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
try {
|
|
259
|
+
await client?.destroy();
|
|
260
|
+
}
|
|
261
|
+
catch (e) {
|
|
262
|
+
process.stderr.write(`[azeth-mcp] destroy error: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=guardian.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardian.js","sourceRoot":"","sources":["../../src/tools/guardian.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
/** Register XMTP messaging MCP tools: azeth_send_message, azeth_check_reachability, azeth_receive_messages, azeth_list_conversations, azeth_discover_agent_capabilities */
|
|
3
|
+
export declare function registerMessagingTools(server: McpServer): void;
|
|
4
|
+
//# sourceMappingURL=messaging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messaging.d.ts","sourceRoot":"","sources":["../../src/tools/messaging.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKzE,2KAA2K;AAC3K,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA0W9D"}
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createClient } from '../utils/client.js';
|
|
3
|
+
import { resolveAddress } from '../utils/resolve.js';
|
|
4
|
+
import { success, error, handleError } from '../utils/response.js';
|
|
5
|
+
/** Register XMTP messaging MCP tools: azeth_send_message, azeth_check_reachability, azeth_receive_messages, azeth_list_conversations, azeth_discover_agent_capabilities */
|
|
6
|
+
export function registerMessagingTools(server) {
|
|
7
|
+
// ──────────────────────────────────────────────
|
|
8
|
+
// azeth_send_message
|
|
9
|
+
// ──────────────────────────────────────────────
|
|
10
|
+
server.registerTool('azeth_send_message', {
|
|
11
|
+
description: [
|
|
12
|
+
'Send an encrypted message to another participant via the XMTP messaging network.',
|
|
13
|
+
'',
|
|
14
|
+
'Use this when: You need to communicate with another agent or service using end-to-end encrypted messaging.',
|
|
15
|
+
'The recipient must be reachable on the XMTP network (use azeth_check_reachability first if unsure).',
|
|
16
|
+
'',
|
|
17
|
+
'The "to" field accepts: an Ethereum address, a participant name, "me", or "#N" (account index).',
|
|
18
|
+
'',
|
|
19
|
+
'Returns: The conversation ID and recipient address confirming delivery.',
|
|
20
|
+
'',
|
|
21
|
+
'Note: This is NOT idempotent — each call sends a new message. The sender account is determined',
|
|
22
|
+
'by the AZETH_PRIVATE_KEY environment variable. Messages are limited to 10,000 characters.',
|
|
23
|
+
'',
|
|
24
|
+
'Example: { "to": "Alice", "content": "Hello, I would like to use your price-feed service." }',
|
|
25
|
+
].join('\n'),
|
|
26
|
+
inputSchema: z.object({
|
|
27
|
+
chain: z.string().optional().describe('Target chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet").'),
|
|
28
|
+
to: z.string().describe('Recipient: Ethereum address, participant name, "me", or "#N" (account index).'),
|
|
29
|
+
content: z.string().min(1).max(10_000).describe('Message text content (1-10,000 characters).'),
|
|
30
|
+
contentType: z.string().max(100).optional().describe('Content type hint. Defaults to "text/plain".'),
|
|
31
|
+
}),
|
|
32
|
+
}, async (args) => {
|
|
33
|
+
let client;
|
|
34
|
+
try {
|
|
35
|
+
client = await createClient(args.chain);
|
|
36
|
+
// Resolve "to": address, name, "me", "#N"
|
|
37
|
+
let toResolved;
|
|
38
|
+
try {
|
|
39
|
+
toResolved = await resolveAddress(args.to, client);
|
|
40
|
+
}
|
|
41
|
+
catch (resolveErr) {
|
|
42
|
+
return handleError(resolveErr);
|
|
43
|
+
}
|
|
44
|
+
const conversationId = await client.sendMessage({
|
|
45
|
+
to: toResolved.address,
|
|
46
|
+
content: args.content,
|
|
47
|
+
contentType: args.contentType,
|
|
48
|
+
});
|
|
49
|
+
return success({
|
|
50
|
+
conversationId,
|
|
51
|
+
to: toResolved.address,
|
|
52
|
+
...(toResolved.resolvedFrom ? { resolvedTo: `"${toResolved.resolvedFrom}" → ${toResolved.address}` } : {}),
|
|
53
|
+
sent: true,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
return handleError(err);
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
try {
|
|
61
|
+
await client?.destroy();
|
|
62
|
+
}
|
|
63
|
+
catch { /* M-10: prevent destroy from masking the original error */ }
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
// ──────────────────────────────────────────────
|
|
67
|
+
// azeth_check_reachability
|
|
68
|
+
// ──────────────────────────────────────────────
|
|
69
|
+
server.registerTool('azeth_check_reachability', {
|
|
70
|
+
description: [
|
|
71
|
+
'Check if an Ethereum address is reachable on the XMTP messaging network.',
|
|
72
|
+
'',
|
|
73
|
+
'Use this when: You want to verify a participant can receive XMTP messages before sending.',
|
|
74
|
+
'This is a read-like operation and safe to retry.',
|
|
75
|
+
'',
|
|
76
|
+
'The "address" field accepts: an Ethereum address, a participant name, "me", or "#N" (account index).',
|
|
77
|
+
'',
|
|
78
|
+
'Returns: The address and whether it is reachable (boolean).',
|
|
79
|
+
'',
|
|
80
|
+
'Note: Reachability is cached for 5 minutes. An address is reachable if it has an active XMTP identity.',
|
|
81
|
+
'The checking account is determined by the AZETH_PRIVATE_KEY environment variable.',
|
|
82
|
+
'',
|
|
83
|
+
'Example: { "address": "Alice" } or { "address": "0x1234567890abcdef1234567890abcdef12345678" }',
|
|
84
|
+
].join('\n'),
|
|
85
|
+
inputSchema: z.object({
|
|
86
|
+
chain: z.string().optional().describe('Target chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet").'),
|
|
87
|
+
address: z.string().describe('Address to check: Ethereum address, participant name, "me", or "#N".'),
|
|
88
|
+
}),
|
|
89
|
+
}, async (args) => {
|
|
90
|
+
let client;
|
|
91
|
+
try {
|
|
92
|
+
client = await createClient(args.chain);
|
|
93
|
+
// Resolve address: address, name, "me", "#N"
|
|
94
|
+
let resolved;
|
|
95
|
+
try {
|
|
96
|
+
resolved = await resolveAddress(args.address, client);
|
|
97
|
+
}
|
|
98
|
+
catch (resolveErr) {
|
|
99
|
+
return handleError(resolveErr);
|
|
100
|
+
}
|
|
101
|
+
const reachable = await client.canReach(resolved.address);
|
|
102
|
+
return success({
|
|
103
|
+
address: resolved.address,
|
|
104
|
+
...(resolved.resolvedFrom ? { resolvedFrom: `"${resolved.resolvedFrom}" → ${resolved.address}` } : {}),
|
|
105
|
+
reachable,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
return handleError(err);
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
try {
|
|
113
|
+
await client?.destroy();
|
|
114
|
+
}
|
|
115
|
+
catch { /* M-10: prevent destroy from masking the original error */ }
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
// ──────────────────────────────────────────────
|
|
119
|
+
// azeth_receive_messages
|
|
120
|
+
// ──────────────────────────────────────────────
|
|
121
|
+
server.registerTool('azeth_receive_messages', {
|
|
122
|
+
description: [
|
|
123
|
+
'Read incoming encrypted messages from the XMTP messaging network.',
|
|
124
|
+
'',
|
|
125
|
+
'Use this when: You want to check for messages from other agents or services.',
|
|
126
|
+
'This is the "inbox" view — it lets you read what others have sent you.',
|
|
127
|
+
'',
|
|
128
|
+
'Two modes:',
|
|
129
|
+
' 1. With "from": Read messages from a specific sender (up to "limit" messages)',
|
|
130
|
+
' 2. Without "from": Read the latest message from each conversation (inbox overview)',
|
|
131
|
+
'',
|
|
132
|
+
'The "from" field accepts: an Ethereum address, a participant name, "me", or "#N" (account index).',
|
|
133
|
+
'',
|
|
134
|
+
'Returns: Array of messages with sender address, content, timestamp, and conversation ID.',
|
|
135
|
+
'',
|
|
136
|
+
'Note: XMTP messages are end-to-end encrypted. The account reading messages is determined',
|
|
137
|
+
'by the AZETH_PRIVATE_KEY environment variable. First call may be slow due to XMTP initialization.',
|
|
138
|
+
'',
|
|
139
|
+
'Example: { "from": "Alice", "limit": 10 } or { } (all conversations)',
|
|
140
|
+
].join('\n'),
|
|
141
|
+
inputSchema: z.object({
|
|
142
|
+
chain: z.string().optional().describe('Target chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet").'),
|
|
143
|
+
from: z.string().optional().describe('Read messages from a specific sender. Accepts: address, name, "me", "#N". Omit for inbox overview.'),
|
|
144
|
+
limit: z.coerce.number().int().min(1).max(100).optional().describe('Maximum messages to return. Defaults to 20. Max 100.'),
|
|
145
|
+
}),
|
|
146
|
+
}, async (args) => {
|
|
147
|
+
let client;
|
|
148
|
+
try {
|
|
149
|
+
client = await createClient(args.chain);
|
|
150
|
+
const limit = args.limit ?? 20;
|
|
151
|
+
if (args.from) {
|
|
152
|
+
// Mode 1: Messages from specific sender
|
|
153
|
+
let fromResolved;
|
|
154
|
+
try {
|
|
155
|
+
fromResolved = await resolveAddress(args.from, client);
|
|
156
|
+
}
|
|
157
|
+
catch (resolveErr) {
|
|
158
|
+
return handleError(resolveErr);
|
|
159
|
+
}
|
|
160
|
+
const messages = await client.getMessages(fromResolved.address, limit);
|
|
161
|
+
return success({
|
|
162
|
+
from: fromResolved.address,
|
|
163
|
+
...(fromResolved.resolvedFrom ? { resolvedFrom: `"${fromResolved.resolvedFrom}" → ${fromResolved.address}` } : {}),
|
|
164
|
+
messageCount: messages.length,
|
|
165
|
+
messages: messages.map(msg => ({
|
|
166
|
+
sender: msg.sender,
|
|
167
|
+
content: msg.content,
|
|
168
|
+
timestamp: new Date(msg.timestamp).toISOString(),
|
|
169
|
+
conversationId: msg.conversationId,
|
|
170
|
+
})),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// Mode 2: Inbox overview — latest message per conversation
|
|
175
|
+
const conversations = await client.getConversations();
|
|
176
|
+
// For each conversation, get the latest message
|
|
177
|
+
const inbox = [];
|
|
178
|
+
for (const conv of conversations.slice(0, limit)) {
|
|
179
|
+
try {
|
|
180
|
+
const messages = await client.getMessages(conv.peerAddress, 1);
|
|
181
|
+
inbox.push({
|
|
182
|
+
conversationId: conv.id,
|
|
183
|
+
peerAddress: conv.peerAddress,
|
|
184
|
+
createdAt: new Date(conv.createdAt).toISOString(),
|
|
185
|
+
latestMessage: messages.length > 0 ? {
|
|
186
|
+
content: messages[0].content,
|
|
187
|
+
sender: messages[0].sender,
|
|
188
|
+
timestamp: new Date(messages[0].timestamp).toISOString(),
|
|
189
|
+
} : null,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Skip conversations that fail to load
|
|
194
|
+
inbox.push({
|
|
195
|
+
conversationId: conv.id,
|
|
196
|
+
peerAddress: conv.peerAddress,
|
|
197
|
+
createdAt: new Date(conv.createdAt).toISOString(),
|
|
198
|
+
latestMessage: null,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return success({
|
|
203
|
+
conversationCount: conversations.length,
|
|
204
|
+
showing: inbox.length,
|
|
205
|
+
inbox,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
return handleError(err);
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
try {
|
|
214
|
+
await client?.destroy();
|
|
215
|
+
}
|
|
216
|
+
catch { /* M-10: prevent destroy from masking the original error */ }
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
// ──────────────────────────────────────────────
|
|
220
|
+
// azeth_list_conversations
|
|
221
|
+
// ──────────────────────────────────────────────
|
|
222
|
+
server.registerTool('azeth_list_conversations', {
|
|
223
|
+
description: [
|
|
224
|
+
'List all active XMTP messaging conversations.',
|
|
225
|
+
'',
|
|
226
|
+
'Use this when: You want to see who you have been communicating with,',
|
|
227
|
+
'or check if a conversation exists with a specific peer.',
|
|
228
|
+
'',
|
|
229
|
+
'Returns: Array of conversations with peer address and creation time.',
|
|
230
|
+
'',
|
|
231
|
+
'Note: First call may be slow due to XMTP initialization.',
|
|
232
|
+
'',
|
|
233
|
+
'Example: { }',
|
|
234
|
+
].join('\n'),
|
|
235
|
+
inputSchema: z.object({
|
|
236
|
+
chain: z.string().optional().describe('Target chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet").'),
|
|
237
|
+
}),
|
|
238
|
+
}, async (args) => {
|
|
239
|
+
let client;
|
|
240
|
+
try {
|
|
241
|
+
client = await createClient(args.chain);
|
|
242
|
+
const conversations = await client.getConversations();
|
|
243
|
+
return success({
|
|
244
|
+
conversationCount: conversations.length,
|
|
245
|
+
conversations: conversations.map(conv => ({
|
|
246
|
+
id: conv.id,
|
|
247
|
+
peerAddress: conv.peerAddress,
|
|
248
|
+
createdAt: new Date(conv.createdAt).toISOString(),
|
|
249
|
+
})),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
return handleError(err);
|
|
254
|
+
}
|
|
255
|
+
finally {
|
|
256
|
+
try {
|
|
257
|
+
await client?.destroy();
|
|
258
|
+
}
|
|
259
|
+
catch { /* M-10: prevent destroy from masking the original error */ }
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
// ──────────────────────────────────────────────
|
|
263
|
+
// azeth_discover_agent_capabilities
|
|
264
|
+
// ──────────────────────────────────────────────
|
|
265
|
+
server.registerTool('azeth_discover_agent_capabilities', {
|
|
266
|
+
description: [
|
|
267
|
+
'Discover what services an agent offers by sending them a capabilities request over XMTP.',
|
|
268
|
+
'',
|
|
269
|
+
'Use this when: You want to find out what services another agent provides, their pricing,',
|
|
270
|
+
'and how to use them — before making a service request or payment.',
|
|
271
|
+
'',
|
|
272
|
+
'Sends a JSON capabilities request to the target agent and waits for their response.',
|
|
273
|
+
'The target agent must be online and have a MessageRouter configured to respond.',
|
|
274
|
+
'',
|
|
275
|
+
'The "agentAddress" field accepts: an Ethereum address, a participant name, "me", or "#N" (account index).',
|
|
276
|
+
'',
|
|
277
|
+
'Returns: The agent\'s capabilities including services, pricing, and usage instructions.',
|
|
278
|
+
'If no response within the timeout, returns an error indicating the agent may be offline.',
|
|
279
|
+
'',
|
|
280
|
+
'Example: { "agentAddress": "0x1234567890abcdef1234567890abcdef12345678" }',
|
|
281
|
+
].join('\n'),
|
|
282
|
+
inputSchema: z.object({
|
|
283
|
+
chain: z.string().optional().describe('Target chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet").'),
|
|
284
|
+
agentAddress: z.string().describe('Target agent: Ethereum address, participant name, "me", or "#N" (account index).'),
|
|
285
|
+
timeoutMs: z.coerce.number().int().min(1000).max(60_000).optional().describe('Timeout in milliseconds to wait for response. Defaults to 15000 (15 seconds). Max 60000.'),
|
|
286
|
+
}),
|
|
287
|
+
}, async (args) => {
|
|
288
|
+
let client;
|
|
289
|
+
try {
|
|
290
|
+
client = await createClient(args.chain);
|
|
291
|
+
const timeoutMs = args.timeoutMs ?? 15_000;
|
|
292
|
+
// Resolve target address
|
|
293
|
+
let resolved;
|
|
294
|
+
try {
|
|
295
|
+
resolved = await resolveAddress(args.agentAddress, client);
|
|
296
|
+
}
|
|
297
|
+
catch (resolveErr) {
|
|
298
|
+
return handleError(resolveErr);
|
|
299
|
+
}
|
|
300
|
+
// Send capabilities request
|
|
301
|
+
const capabilitiesRequest = JSON.stringify({ type: 'capabilities' });
|
|
302
|
+
await client.sendMessage({
|
|
303
|
+
to: resolved.address,
|
|
304
|
+
content: capabilitiesRequest,
|
|
305
|
+
});
|
|
306
|
+
// Poll for response within timeout
|
|
307
|
+
const startTime = Date.now();
|
|
308
|
+
const pollIntervalMs = 2_000;
|
|
309
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
310
|
+
// Wait before polling
|
|
311
|
+
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
312
|
+
const messages = await client.getMessages(resolved.address, 5);
|
|
313
|
+
if (messages.length > 0) {
|
|
314
|
+
// Look for a capabilities response among recent messages
|
|
315
|
+
for (const msg of messages) {
|
|
316
|
+
// Skip messages older than our request
|
|
317
|
+
if (msg.timestamp < startTime)
|
|
318
|
+
continue;
|
|
319
|
+
try {
|
|
320
|
+
const parsed = JSON.parse(msg.content);
|
|
321
|
+
if (typeof parsed === 'object' &&
|
|
322
|
+
parsed !== null &&
|
|
323
|
+
'type' in parsed &&
|
|
324
|
+
parsed.type === 'capabilities') {
|
|
325
|
+
return success({
|
|
326
|
+
agentAddress: resolved.address,
|
|
327
|
+
...(resolved.resolvedFrom ? { resolvedFrom: `"${resolved.resolvedFrom}" → ${resolved.address}` } : {}),
|
|
328
|
+
capabilities: parsed,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
// Not valid JSON — skip
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Timeout — no response received
|
|
339
|
+
return error('NETWORK_ERROR', `No capabilities response received from ${resolved.address} within ${timeoutMs}ms. ` +
|
|
340
|
+
'The agent may be offline or does not have a MessageRouter configured.');
|
|
341
|
+
}
|
|
342
|
+
catch (err) {
|
|
343
|
+
return handleError(err);
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
try {
|
|
347
|
+
await client?.destroy();
|
|
348
|
+
}
|
|
349
|
+
catch { /* M-10: prevent destroy from masking the original error */ }
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=messaging.js.map
|