@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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +48 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/tools/account.d.ts +4 -0
  8. package/dist/tools/account.d.ts.map +1 -0
  9. package/dist/tools/account.js +640 -0
  10. package/dist/tools/account.js.map +1 -0
  11. package/dist/tools/agreements.d.ts +4 -0
  12. package/dist/tools/agreements.d.ts.map +1 -0
  13. package/dist/tools/agreements.js +865 -0
  14. package/dist/tools/agreements.js.map +1 -0
  15. package/dist/tools/guardian-approval.d.ts +4 -0
  16. package/dist/tools/guardian-approval.d.ts.map +1 -0
  17. package/dist/tools/guardian-approval.js +319 -0
  18. package/dist/tools/guardian-approval.js.map +1 -0
  19. package/dist/tools/guardian.d.ts +4 -0
  20. package/dist/tools/guardian.d.ts.map +1 -0
  21. package/dist/tools/guardian.js +267 -0
  22. package/dist/tools/guardian.js.map +1 -0
  23. package/dist/tools/messaging.d.ts +4 -0
  24. package/dist/tools/messaging.d.ts.map +1 -0
  25. package/dist/tools/messaging.js +353 -0
  26. package/dist/tools/messaging.js.map +1 -0
  27. package/dist/tools/payments.d.ts +14 -0
  28. package/dist/tools/payments.d.ts.map +1 -0
  29. package/dist/tools/payments.js +723 -0
  30. package/dist/tools/payments.js.map +1 -0
  31. package/dist/tools/registry.d.ts +4 -0
  32. package/dist/tools/registry.d.ts.map +1 -0
  33. package/dist/tools/registry.js +608 -0
  34. package/dist/tools/registry.js.map +1 -0
  35. package/dist/tools/reputation.d.ts +4 -0
  36. package/dist/tools/reputation.d.ts.map +1 -0
  37. package/dist/tools/reputation.js +433 -0
  38. package/dist/tools/reputation.js.map +1 -0
  39. package/dist/tools/transfer.d.ts +4 -0
  40. package/dist/tools/transfer.d.ts.map +1 -0
  41. package/dist/tools/transfer.js +181 -0
  42. package/dist/tools/transfer.js.map +1 -0
  43. package/dist/utils/client.d.ts +25 -0
  44. package/dist/utils/client.d.ts.map +1 -0
  45. package/dist/utils/client.js +100 -0
  46. package/dist/utils/client.js.map +1 -0
  47. package/dist/utils/error-selectors.d.ts +23 -0
  48. package/dist/utils/error-selectors.d.ts.map +1 -0
  49. package/dist/utils/error-selectors.js +159 -0
  50. package/dist/utils/error-selectors.js.map +1 -0
  51. package/dist/utils/rate-limit.d.ts +17 -0
  52. package/dist/utils/rate-limit.d.ts.map +1 -0
  53. package/dist/utils/rate-limit.js +75 -0
  54. package/dist/utils/rate-limit.js.map +1 -0
  55. package/dist/utils/resolve.d.ts +38 -0
  56. package/dist/utils/resolve.d.ts.map +1 -0
  57. package/dist/utils/resolve.js +308 -0
  58. package/dist/utils/resolve.js.map +1 -0
  59. package/dist/utils/response.d.ts +42 -0
  60. package/dist/utils/response.d.ts.map +1 -0
  61. package/dist/utils/response.js +257 -0
  62. package/dist/utils/response.js.map +1 -0
  63. package/package.json +62 -0
@@ -0,0 +1,181 @@
1
+ import { z } from 'zod';
2
+ import { parseEther, parseUnits, formatEther, erc20Abi } from 'viem';
3
+ import { createClient, validateAddress } from '../utils/client.js';
4
+ import { resolveAddress, resolveSmartAccount } from '../utils/resolve.js';
5
+ import { success, error, handleError, guardianRequiredError } from '../utils/response.js';
6
+ /** Register the azeth_transfer MCP tool */
7
+ export function registerTransferTools(server) {
8
+ server.registerTool('azeth_transfer', {
9
+ description: [
10
+ 'Send ETH or ERC-20 tokens FROM your Azeth smart account to another address.',
11
+ '',
12
+ 'Use this when: You need to pay another participant, fund an account, or move tokens between addresses.',
13
+ '',
14
+ 'The "to" field accepts: an Ethereum address, a participant name (resolved via trust registry),',
15
+ '"me" (your first smart account), or "#N" (Nth account index from azeth_accounts).',
16
+ '',
17
+ 'IMPORTANT: This sends FROM your smart account, not your EOA. Ensure your smart account is funded.',
18
+ 'Use azeth_deposit first to fund your smart account if needed.',
19
+ 'One EOA can own multiple smart accounts — specify which one, or defaults to first.',
20
+ '',
21
+ 'Returns: Transaction hash, sender smart account address, recipient address (with resolution info), and amount sent.',
22
+ '',
23
+ 'Note: This is a state-changing operation. The tool shows the resolved address before executing.',
24
+ 'For ETH transfers, omit the token parameter. For ERC-20 tokens, provide the token contract address AND decimals.',
25
+ 'The amount is in human-readable units (e.g., "1.5" for 1.5 ETH or "100" for 100 USDC).',
26
+ 'The sender account is determined by the AZETH_PRIVATE_KEY environment variable.',
27
+ '',
28
+ 'Example: { "to": "Alice", "amount": "0.001" } or { "to": "0x1234...abcd", "amount": "10", "token": "0x036C...CF7e", "decimals": 6 }',
29
+ ].join('\n'),
30
+ inputSchema: z.object({
31
+ 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").'),
32
+ to: z.string().describe('Recipient: Ethereum address, participant name, "me", or "#N" (account index).'),
33
+ amount: z.string().describe('Amount to send in human-readable units (e.g., "1.5" for 1.5 ETH, "100" for 100 USDC).'),
34
+ token: z.string().optional().describe('ERC-20 token contract address. Omit for native ETH transfer.'),
35
+ decimals: z.coerce.number().int().min(0).max(18).optional().describe('Token decimals for ERC-20 transfers. REQUIRED when token is specified. Use 6 for USDC, 18 for WETH.'),
36
+ smartAccount: z.string().optional().describe('Smart account to transfer from: address, name, or "#N". If omitted, uses your first smart account.'),
37
+ }),
38
+ }, async (args) => {
39
+ if (args.token && !validateAddress(args.token)) {
40
+ return error('INVALID_INPUT', `Invalid token address: "${args.token}".`, 'Must be 0x-prefixed followed by 40 hex characters.');
41
+ }
42
+ if (args.token && args.decimals === undefined) {
43
+ return error('INVALID_INPUT', 'decimals is required when token address is provided.', 'Use 6 for USDC, 18 for WETH.');
44
+ }
45
+ let client;
46
+ try {
47
+ client = await createClient(args.chain);
48
+ // Resolve "to": address, name, "me", "#N"
49
+ let toResolved;
50
+ try {
51
+ toResolved = await resolveAddress(args.to, client, 'account');
52
+ }
53
+ catch (resolveErr) {
54
+ return handleError(resolveErr);
55
+ }
56
+ // Resolve smartAccount: address, name, "#N"
57
+ let fromAccount;
58
+ if (args.smartAccount) {
59
+ try {
60
+ fromAccount = await resolveSmartAccount(args.smartAccount, client);
61
+ }
62
+ catch (resolveErr) {
63
+ return handleError(resolveErr);
64
+ }
65
+ }
66
+ const tokenAddress = args.token;
67
+ const decimals = args.decimals ?? 18;
68
+ let amount;
69
+ try {
70
+ amount = tokenAddress
71
+ ? parseUnits(args.amount, decimals)
72
+ : parseEther(args.amount);
73
+ }
74
+ catch {
75
+ return error('INVALID_INPUT', 'Invalid amount format — must be a valid decimal number');
76
+ }
77
+ // Pre-flight: check balance before submitting UserOp
78
+ try {
79
+ const senderAccount = fromAccount ?? await client.resolveSmartAccount();
80
+ if (tokenAddress) {
81
+ // Direct ERC-20 balanceOf call — getBalance() keys by symbol, not address
82
+ const available = await client.publicClient.readContract({
83
+ address: tokenAddress,
84
+ abi: erc20Abi,
85
+ functionName: 'balanceOf',
86
+ args: [senderAccount],
87
+ });
88
+ if (available < amount) {
89
+ const { formatUnits } = await import('viem');
90
+ return error('INSUFFICIENT_BALANCE', `Insufficient token balance: have ${formatUnits(available, decimals)}, need ${args.amount}.`, `Fund your smart account (${senderAccount}) before retrying.`);
91
+ }
92
+ }
93
+ else {
94
+ const balance = await client.getBalance(senderAccount);
95
+ if (balance.eth < amount) {
96
+ return error('INSUFFICIENT_BALANCE', `Insufficient ETH balance: have ${formatEther(balance.eth)} ETH, need ${args.amount} ETH.`, `Fund your smart account (${fromAccount ?? senderAccount}) before retrying.`);
97
+ }
98
+ }
99
+ }
100
+ catch {
101
+ // Balance check is best-effort; proceed and let the bundler return details on failure
102
+ }
103
+ const result = await client.transfer({ to: toResolved.address, amount, token: tokenAddress }, fromAccount);
104
+ // Enrich response with transaction receipt data (gas, events)
105
+ let receiptData = {};
106
+ try {
107
+ const receipt = await client.publicClient.getTransactionReceipt({ hash: result.txHash });
108
+ const gasUsed = receipt.gasUsed;
109
+ const effectiveGasPrice = receipt.effectiveGasPrice;
110
+ const gasCostWei = gasUsed * effectiveGasPrice;
111
+ const { formatTokenAmount } = await import('@azeth/common');
112
+ // Decode known events from logs
113
+ const events = [];
114
+ try {
115
+ const { decodeEventLog } = await import('viem');
116
+ const { GuardianModuleAbi, ReputationModuleAbi: RepAbi } = await import('@azeth/common/abis');
117
+ const knownAbis = [GuardianModuleAbi, RepAbi];
118
+ for (const log of receipt.logs) {
119
+ for (const abi of knownAbis) {
120
+ try {
121
+ const decoded = decodeEventLog({ abi, data: log.data, topics: log.topics });
122
+ const stringArgs = {};
123
+ for (const [k, v] of Object.entries(decoded.args)) {
124
+ stringArgs[k] = typeof v === 'bigint' ? v.toString() : String(v);
125
+ }
126
+ events.push({ name: decoded.eventName, args: stringArgs });
127
+ break; // matched this log
128
+ }
129
+ catch {
130
+ // This ABI doesn't match this log — try next
131
+ }
132
+ }
133
+ }
134
+ }
135
+ catch {
136
+ // Event decoding failure is non-fatal
137
+ }
138
+ receiptData = {
139
+ gasUsed: gasUsed.toString(),
140
+ gasCostETH: formatTokenAmount(gasCostWei, 18, 8),
141
+ ...(events.length > 0 ? { events } : {}),
142
+ };
143
+ }
144
+ catch {
145
+ // Receipt fetch failure is non-fatal
146
+ }
147
+ return success({
148
+ txHash: result.txHash,
149
+ from: result.from,
150
+ to: result.to,
151
+ amount: args.amount,
152
+ token: result.token,
153
+ ...(toResolved.resolvedFrom ? {
154
+ resolvedTo: `"${toResolved.resolvedFrom}" → ${toResolved.address}`,
155
+ ...(toResolved.name ? { resolvedName: toResolved.name } : {}),
156
+ ...(toResolved.tokenId ? { resolvedTokenId: toResolved.tokenId } : {}),
157
+ } : {}),
158
+ ...receiptData,
159
+ }, { txHash: result.txHash });
160
+ }
161
+ catch (err) {
162
+ if (err instanceof Error && /AA24/.test(err.message)) {
163
+ return guardianRequiredError('Transfer amount exceeds your standard spending limit.', {
164
+ operation: 'transfer',
165
+ amount: `${args.amount} ${args.token ? 'tokens' : 'ETH'}`,
166
+ limit: 'Check with azeth_get_guardrails',
167
+ });
168
+ }
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
+ //# sourceMappingURL=transfer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../src/tools/transfer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { AzethKit } from '@azeth/sdk';
2
+ import { resolveViemChain, type SupportedChainName } from '@azeth/common';
3
+ /** Resolve a chain argument to a canonical SupportedChainName.
4
+ * Resolution order: explicit arg > AZETH_CHAIN env > 'baseSepolia' default.
5
+ * Accepts aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet".
6
+ */
7
+ export declare function resolveChain(argChain?: string): SupportedChainName;
8
+ export { resolveViemChain };
9
+ /** Create an AzethKit instance using the private key from the AZETH_PRIVATE_KEY
10
+ * environment variable. The private key is NEVER accepted as a tool parameter.
11
+ *
12
+ * After creation, attempts to resolve existing smart account(s) from the factory.
13
+ * If no accounts exist yet, the smart account will be null (createAccount() sets it).
14
+ *
15
+ * LOW-6 (Audit): A new AzethKit instance is created per MCP tool call. This is intentionally
16
+ * stateless — each call gets a fresh client with no shared mutable state, which prevents
17
+ * cross-request contamination and simplifies error recovery. For production optimization,
18
+ * a singleton pattern with health-check reconnection could reduce RPC setup overhead per call.
19
+ */
20
+ export declare function createClient(chain?: SupportedChainName | string): Promise<AzethKit>;
21
+ /** Validate that a string looks like a hex private key */
22
+ export declare function validatePrivateKey(key: string): key is `0x${string}`;
23
+ /** Validate that a string looks like an Ethereum address */
24
+ export declare function validateAddress(addr: string): addr is `0x${string}`;
25
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/utils/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAuB,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAiC,gBAAgB,EAAmB,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAE1H;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAYlE;AAGD,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,KAAK,CAAC,EAAE,kBAAkB,GAAG,MAAM,GAClC,OAAO,CAAC,QAAQ,CAAC,CAgFnB;AAED,0DAA0D;AAC1D,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,KAAK,MAAM,EAAE,CAEpE;AAED,4DAA4D;AAC5D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,KAAK,MAAM,EAAE,CAEnE"}
@@ -0,0 +1,100 @@
1
+ import { AzethKit } from '@azeth/sdk';
2
+ import { AzethError, resolveChainAlias, resolveViemChain } from '@azeth/common';
3
+ /** Resolve a chain argument to a canonical SupportedChainName.
4
+ * Resolution order: explicit arg > AZETH_CHAIN env > 'baseSepolia' default.
5
+ * Accepts aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet".
6
+ */
7
+ export function resolveChain(argChain) {
8
+ const raw = argChain ?? process.env['AZETH_CHAIN'];
9
+ if (!raw)
10
+ return 'baseSepolia';
11
+ const canonical = resolveChainAlias(raw);
12
+ if (!canonical) {
13
+ throw new AzethError(`Unknown chain "${raw}". Supported: "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet").`, 'INVALID_INPUT');
14
+ }
15
+ return canonical;
16
+ }
17
+ // Re-export for consumers within mcp-server that may still import from here
18
+ export { resolveViemChain };
19
+ /** Create an AzethKit instance using the private key from the AZETH_PRIVATE_KEY
20
+ * environment variable. The private key is NEVER accepted as a tool parameter.
21
+ *
22
+ * After creation, attempts to resolve existing smart account(s) from the factory.
23
+ * If no accounts exist yet, the smart account will be null (createAccount() sets it).
24
+ *
25
+ * LOW-6 (Audit): A new AzethKit instance is created per MCP tool call. This is intentionally
26
+ * stateless — each call gets a fresh client with no shared mutable state, which prevents
27
+ * cross-request contamination and simplifies error recovery. For production optimization,
28
+ * a singleton pattern with health-check reconnection could reduce RPC setup overhead per call.
29
+ */
30
+ export async function createClient(chain) {
31
+ const privateKey = process.env['AZETH_PRIVATE_KEY'];
32
+ if (!privateKey) {
33
+ throw new AzethError('AZETH_PRIVATE_KEY environment variable is required. Set it before using Azeth tools.', 'UNAUTHORIZED');
34
+ }
35
+ if (!validatePrivateKey(privateKey)) {
36
+ throw new AzethError('AZETH_PRIVATE_KEY is malformed. Must be 0x-prefixed followed by 64 hex characters.', 'UNAUTHORIZED');
37
+ }
38
+ // Guardian co-signing key (optional — enables auto-approval for operations exceeding spending limits)
39
+ const guardianKey = process.env['AZETH_GUARDIAN_KEY'];
40
+ if (guardianKey && !validatePrivateKey(guardianKey)) {
41
+ throw new AzethError('AZETH_GUARDIAN_KEY is malformed. Must be 0x-prefixed followed by 64 hex characters.', 'UNAUTHORIZED');
42
+ }
43
+ const resolvedChain = resolveChain(chain);
44
+ // Guardian auto-sign: must be explicitly set to "true" to enable.
45
+ // When false (default), operations exceeding limits require interactive guardian approval.
46
+ const guardianAutoSign = process.env['AZETH_GUARDIAN_AUTO_SIGN']?.toLowerCase() === 'true';
47
+ const config = {
48
+ privateKey: privateKey,
49
+ chain: resolvedChain,
50
+ serverUrl: process.env['AZETH_SERVER_URL'],
51
+ guardianKey: guardianKey,
52
+ guardianAutoSign,
53
+ };
54
+ const rpcUrl = process.env['AZETH_RPC_URL'];
55
+ if (rpcUrl) {
56
+ config.rpcUrl = rpcUrl;
57
+ }
58
+ const bundlerUrl = process.env['AZETH_BUNDLER_URL'];
59
+ if (bundlerUrl) {
60
+ config.bundlerUrl = bundlerUrl;
61
+ }
62
+ const paymasterUrl = process.env['AZETH_PAYMASTER_URL'];
63
+ if (paymasterUrl) {
64
+ config.paymasterUrl = paymasterUrl;
65
+ }
66
+ // Wire XMTP config from env vars for persistent installations.
67
+ // Without a persistent encryption key, each MCP call creates a new XMTP installation
68
+ // which quickly exhausts the 10-installation-per-inbox limit.
69
+ const xmtpEncryptionKey = process.env['XMTP_ENCRYPTION_KEY'];
70
+ if (xmtpEncryptionKey) {
71
+ const xmtpConfig = {
72
+ dbEncryptionKey: xmtpEncryptionKey,
73
+ env: process.env['XMTP_ENV'] ?? 'production',
74
+ };
75
+ const xmtpDbPath = process.env['XMTP_DB_PATH'];
76
+ if (xmtpDbPath) {
77
+ xmtpConfig.dbPath = xmtpDbPath;
78
+ }
79
+ config.xmtp = xmtpConfig;
80
+ }
81
+ const client = await AzethKit.create(config);
82
+ // Auto-resolve smart account(s) if any exist on-chain.
83
+ // Non-fatal: if no account exists yet, callers will get null from .smartAccount
84
+ try {
85
+ await client.getSmartAccounts();
86
+ }
87
+ catch {
88
+ // No smart accounts deployed yet — fine, createAccount() will set them
89
+ }
90
+ return client;
91
+ }
92
+ /** Validate that a string looks like a hex private key */
93
+ export function validatePrivateKey(key) {
94
+ return /^0x[0-9a-fA-F]{64}$/.test(key.trim());
95
+ }
96
+ /** Validate that a string looks like an Ethereum address */
97
+ export function validateAddress(addr) {
98
+ return /^0x[0-9a-fA-F]{40}$/.test(addr.trim());
99
+ }
100
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/utils/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Pre-computed 4-byte error selectors for Azeth contracts.
3
+ * Maps selector hex (0x + 8 chars) → human-readable name + description.
4
+ *
5
+ * Selectors are derived from keccak256 of each custom error signature.
6
+ * Pre-computed to avoid runtime dependency on viem's keccak256 (which
7
+ * breaks in test environments where viem is mocked).
8
+ *
9
+ * To regenerate: `pnpm exec tsx scripts/compute-selectors.ts`
10
+ */
11
+ /** Attempt to decode a 4-byte error selector from an error message string.
12
+ * Returns a human-readable description if a known selector is found.
13
+ *
14
+ * Handles three cases:
15
+ * 1. Standalone selectors: 0x1f8f95a0
16
+ * 2. Outer selector of long hex: first 8 chars of 0x1f8f95a0000000...
17
+ * 3. Inner selectors in ABI-encoded revert data: EntryPoint wraps module errors
18
+ * in FailedOpWithRevert(uint256,string,bytes), so the actual Azeth error
19
+ * selector is buried inside the hex at a 32-byte ABI boundary.
20
+ * 4. Error(string) decoding: extracts the revert string from standard Solidity reverts
21
+ */
22
+ export declare function decodeErrorSelector(message: string): string | undefined;
23
+ //# sourceMappingURL=error-selectors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-selectors.d.ts","sourceRoot":"","sources":["../../src/utils/error-selectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA2EH;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CA4BvE"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Pre-computed 4-byte error selectors for Azeth contracts.
3
+ * Maps selector hex (0x + 8 chars) → human-readable name + description.
4
+ *
5
+ * Selectors are derived from keccak256 of each custom error signature.
6
+ * Pre-computed to avoid runtime dependency on viem's keccak256 (which
7
+ * breaks in test environments where viem is mocked).
8
+ *
9
+ * To regenerate: `pnpm exec tsx scripts/compute-selectors.ts`
10
+ */
11
+ /** Pre-computed selector → error info */
12
+ const SELECTOR_MAP = {
13
+ // AzethAccount
14
+ '0xacfdb444': { name: 'ExecutionFailed', description: 'Smart account execution failed.' },
15
+ '0x5fc483c5': { name: 'OnlyOwner', description: 'Only the account owner can perform this action.' },
16
+ '0xbd07c551': { name: 'OnlyEntryPoint', description: 'Only the EntryPoint contract can call this function.' },
17
+ '0x7fb6be02': { name: 'OnlyExecutor', description: 'Only an installed executor module can call this function.' },
18
+ '0xea8e4eb5': { name: 'NotAuthorized', description: 'The caller is not authorized for this operation.' },
19
+ '0x49e27cff': { name: 'InvalidOwner', description: 'Invalid owner address.' },
20
+ '0x17e37b5c': { name: 'BatchLengthMismatch', description: 'Batch call arrays have mismatched lengths.' },
21
+ '0xcfc917be': { name: 'MaxHooksReached', description: 'Maximum number of hooks already installed.' },
22
+ '0xd393448a': { name: 'MismatchModuleTypeId', description: 'Module type ID does not match the expected type.' },
23
+ '0x172c3c6a': { name: 'ModuleAlreadyInstalled', description: 'This module is already installed on the account.' },
24
+ '0xbe601672': { name: 'ModuleNotInstalled', description: 'This module is not installed on the account.' },
25
+ // GuardianModule
26
+ '0xaf9aa1e0': { name: 'NotSmartAccount', description: 'The caller is not a recognized Azeth smart account.' },
27
+ '0xef6d0f02': { name: 'NotGuardian', description: 'Only the guardian can perform this operation.' },
28
+ '0x5684d698': { name: 'InvalidGuardrails', description: 'The guardrail parameters are invalid.' },
29
+ '0xa3fef2f8': { name: 'NoPendingChange', description: 'No guardrail change is pending.' },
30
+ '0x621e25c3': { name: 'TimelockNotExpired', description: 'The timelock period has not elapsed yet.' },
31
+ '0x0bbfcdcc': { name: 'NotTightening', description: 'Only tightening (reducing limits) can bypass the timelock.' },
32
+ '0x6320ab2b': { name: 'NoPendingEmergency', description: 'No emergency withdrawal is pending.' },
33
+ '0x4806710a': { name: 'ChangeAlreadyPending', description: 'A guardrail change is already pending.' },
34
+ '0x24831e77': { name: 'ExecutorSpendExceedsLimit', description: 'Payment amount exceeds the guardian daily spend limit.' },
35
+ // PaymentAgreementModule
36
+ '0x95a68634': { name: 'SelfAgreement', description: 'Cannot create a payment agreement with yourself.' },
37
+ '0xf84835a0': { name: 'TokenNotWhitelisted', description: 'The token is not in the guardian whitelist. Add it via setTokenWhitelist.' },
38
+ '0xb5c6c3ab': { name: 'AgreementNotExists', description: 'The specified agreement does not exist.' },
39
+ '0xfe1da89a': { name: 'InvalidAgreement', description: 'The agreement is invalid (already cancelled or completed).' },
40
+ '0x9563bcf0': { name: 'GuardianLimitExceeded', description: 'The payment exceeds guardian spending limits.' },
41
+ '0x90b8ec18': { name: 'TransferFailed', description: 'The token transfer failed.' },
42
+ // ReputationModule
43
+ '0xae525b83': { name: 'InsufficientPaymentUSD', description: 'You must pay at least $1 USD to the target before rating. Use azeth_get_net_paid to check your payment history.' },
44
+ '0xcb02f599': { name: 'SelfRatingNotAllowed', description: 'You cannot rate yourself.' },
45
+ '0x645c0b06': { name: 'SiblingRatingNotAllowed', description: 'You cannot rate accounts owned by the same EOA.' },
46
+ '0x565c8a5a': { name: 'InvalidValueDecimals', description: 'Value decimals must be between 0 and 18.' },
47
+ '0xe6394a77': { name: 'InvalidAgentId', description: 'The agent ID is not registered in the trust registry.' },
48
+ '0x15bebd27': { name: 'NotAzethAccount', description: 'The caller is not an Azeth smart account deployed by the factory.' },
49
+ // TrustRegistryModule
50
+ '0x3a81d6fc': { name: 'AlreadyRegistered', description: 'This account is already registered in the trust registry.' },
51
+ '0xaba47339': { name: 'NotRegistered', description: 'This account is not registered in the trust registry.' },
52
+ '0x3ba01911': { name: 'InvalidURI', description: 'The metadata URI is invalid.' },
53
+ // Factory
54
+ '0x30116425': { name: 'DeploymentFailed', description: 'Smart account deployment failed.' },
55
+ '0x367e9639': { name: 'AccountAlreadyDeployed', description: 'A smart account is already deployed at this address.' },
56
+ '0x99d0d56b': { name: 'MaxAccountsPerOwnerReached', description: 'Maximum accounts per owner has been reached.' },
57
+ // Oracle
58
+ '0x1f8f95a0': { name: 'InvalidOraclePrice', description: 'The oracle returned an invalid price.' },
59
+ '0xbf16aab6': { name: 'UnsupportedToken', description: 'The oracle does not support this token.' },
60
+ '0xcf479181': { name: 'InsufficientBalance', description: 'The smart account has insufficient token balance for this operation.' },
61
+ // Common (shared across multiple contracts)
62
+ '0x0dc149f0': { name: 'AlreadyInitialized', description: 'This module is already initialized for the account.' },
63
+ '0x87138d5c': { name: 'NotInitialized', description: 'The account has not been initialized on this module.' },
64
+ '0xe6c4247b': { name: 'InvalidAddress', description: 'An invalid address was provided.' },
65
+ '0x0c6d42ae': { name: 'OnlyFactory', description: 'Only the AzethFactory can call this function.' },
66
+ };
67
+ /** Well-known Solidity revert selectors (not Azeth-specific) */
68
+ const ERROR_STRING_SELECTOR = '08c379a0'; // Error(string)
69
+ const PANIC_SELECTOR = '4e487b71'; // Panic(uint256)
70
+ /** Attempt to decode a 4-byte error selector from an error message string.
71
+ * Returns a human-readable description if a known selector is found.
72
+ *
73
+ * Handles three cases:
74
+ * 1. Standalone selectors: 0x1f8f95a0
75
+ * 2. Outer selector of long hex: first 8 chars of 0x1f8f95a0000000...
76
+ * 3. Inner selectors in ABI-encoded revert data: EntryPoint wraps module errors
77
+ * in FailedOpWithRevert(uint256,string,bytes), so the actual Azeth error
78
+ * selector is buried inside the hex at a 32-byte ABI boundary.
79
+ * 4. Error(string) decoding: extracts the revert string from standard Solidity reverts
80
+ */
81
+ export function decodeErrorSelector(message) {
82
+ const hexMatches = message.matchAll(/0x([0-9a-fA-F]{8,})/g);
83
+ for (const match of hexMatches) {
84
+ const hexData = match[1].toLowerCase();
85
+ // Pass 1: Check the outer selector (first 8 chars)
86
+ const outerSelector = `0x${hexData.slice(0, 8)}`;
87
+ const outerKnown = SELECTOR_MAP[outerSelector];
88
+ if (outerKnown)
89
+ return `${outerKnown.name}: ${outerKnown.description}`;
90
+ // Pass 1b: Try to decode Error(string) at the outer level
91
+ const outerString = tryDecodeErrorString(hexData);
92
+ if (outerString)
93
+ return outerString;
94
+ // Pass 2: Scan interior at 64-char (32-byte) ABI word boundaries for known
95
+ // selectors. ERC-4337 EntryPoint wraps inner reverts in FailedOpWithRevert
96
+ // where the actual error selector is ABI-encoded at a word boundary.
97
+ for (let i = 64; i + 8 <= hexData.length; i += 64) {
98
+ const innerSelector = `0x${hexData.slice(i, i + 8)}`;
99
+ const innerKnown = SELECTOR_MAP[innerSelector];
100
+ if (innerKnown)
101
+ return `${innerKnown.name}: ${innerKnown.description}`;
102
+ // Try to decode inner Error(string) — common in EntryPoint FailedOpWithRevert
103
+ const innerString = tryDecodeErrorString(hexData.slice(i));
104
+ if (innerString)
105
+ return innerString;
106
+ }
107
+ }
108
+ return undefined;
109
+ }
110
+ /** Try to ABI-decode an Error(string) or Panic(uint256) from raw hex data.
111
+ * Returns a human-readable string if successful, undefined otherwise.
112
+ *
113
+ * Error(string) layout: 08c379a0 + offset(32B) + length(32B) + utf8 data
114
+ * Panic(uint256) layout: 4e487b71 + code(32B)
115
+ */
116
+ function tryDecodeErrorString(hexData) {
117
+ const selector = hexData.slice(0, 8);
118
+ if (selector === ERROR_STRING_SELECTOR && hexData.length >= 8 + 128) {
119
+ // Error(string): skip selector(8) + offset(64), read length(64), then string data
120
+ try {
121
+ const lengthHex = hexData.slice(72, 136);
122
+ const length = parseInt(lengthHex, 16);
123
+ if (length > 0 && length <= 256 && hexData.length >= 136 + length * 2) {
124
+ const strHex = hexData.slice(136, 136 + length * 2);
125
+ const bytes = new Uint8Array(length);
126
+ for (let i = 0; i < length; i++) {
127
+ bytes[i] = parseInt(strHex.slice(i * 2, i * 2 + 2), 16);
128
+ }
129
+ const decoded = new TextDecoder().decode(bytes);
130
+ // Only return if it's printable ASCII (not garbage)
131
+ if (/^[\x20-\x7E]+$/.test(decoded)) {
132
+ return `Revert: ${decoded}`;
133
+ }
134
+ }
135
+ }
136
+ catch {
137
+ // Malformed — fall through
138
+ }
139
+ }
140
+ if (selector === PANIC_SELECTOR && hexData.length >= 72) {
141
+ const codeHex = hexData.slice(8, 72);
142
+ const code = parseInt(codeHex, 16);
143
+ const panicReasons = {
144
+ 0x00: 'generic compiler panic',
145
+ 0x01: 'assertion failed',
146
+ 0x11: 'arithmetic overflow/underflow',
147
+ 0x12: 'division by zero',
148
+ 0x21: 'invalid enum value',
149
+ 0x22: 'invalid storage encoding',
150
+ 0x31: 'empty array pop',
151
+ 0x32: 'array index out of bounds',
152
+ 0x41: 'out of memory',
153
+ 0x51: 'invalid internal function call',
154
+ };
155
+ return `Panic(${code}): ${panicReasons[code] ?? 'unknown panic code'}`;
156
+ }
157
+ return undefined;
158
+ }
159
+ //# sourceMappingURL=error-selectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-selectors.js","sourceRoot":"","sources":["../../src/utils/error-selectors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export interface RateLimiter {
3
+ /** Returns true if the call is allowed, false if rate-limited */
4
+ check(toolName: string): boolean;
5
+ /** Reset all state (for testing) */
6
+ reset(): void;
7
+ /** Stop the periodic cleanup timer (for graceful shutdown / tests) */
8
+ destroy(): void;
9
+ }
10
+ /** Create an in-memory per-tool rate limiter with periodic cleanup */
11
+ export declare function createRateLimiter(maxCalls?: number, windowMs?: number): RateLimiter;
12
+ /**
13
+ * Wrap an McpServer so that each tool registered through the wrapper
14
+ * has per-tool rate limiting applied before the handler executes.
15
+ */
16
+ export declare function wrapServerWithRateLimit(server: McpServer, limiter: RateLimiter): McpServer;
17
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/utils/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASzE,MAAM,WAAW,WAAW;IAC1B,iEAAiE;IACjE,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,oCAAoC;IACpC,KAAK,IAAI,IAAI,CAAC;IACd,sEAAsE;IACtE,OAAO,IAAI,IAAI,CAAC;CACjB;AAKD,sEAAsE;AACtE,wBAAgB,iBAAiB,CAC/B,QAAQ,SAAuB,EAC/B,QAAQ,SAAY,GACnB,WAAW,CAyCb;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,WAAW,GACnB,SAAS,CA8BX"}
@@ -0,0 +1,75 @@
1
+ import { error } from './response.js';
2
+ /** Maximum tool invocations per tool per window */
3
+ const MAX_CALLS_PER_MINUTE = 30;
4
+ /** Sliding window duration in milliseconds */
5
+ const WINDOW_MS = 60_000;
6
+ /** Periodic cleanup interval for expired timestamps (60s) */
7
+ const CLEANUP_INTERVAL_MS = 60_000;
8
+ /** Create an in-memory per-tool rate limiter with periodic cleanup */
9
+ export function createRateLimiter(maxCalls = MAX_CALLS_PER_MINUTE, windowMs = WINDOW_MS) {
10
+ const timestamps = new Map();
11
+ // Periodic cleanup to prevent memory leak from stale tool entries
12
+ const cleanupTimer = setInterval(() => {
13
+ const now = Date.now();
14
+ for (const [tool, times] of timestamps) {
15
+ const recent = times.filter(t => now - t < windowMs);
16
+ if (recent.length === 0) {
17
+ timestamps.delete(tool);
18
+ }
19
+ else {
20
+ timestamps.set(tool, recent);
21
+ }
22
+ }
23
+ }, CLEANUP_INTERVAL_MS);
24
+ // Allow the process to exit without waiting for this timer
25
+ if (cleanupTimer && typeof cleanupTimer === 'object' && 'unref' in cleanupTimer) {
26
+ cleanupTimer.unref();
27
+ }
28
+ return {
29
+ check(toolName) {
30
+ const now = Date.now();
31
+ const existing = timestamps.get(toolName) ?? [];
32
+ const recent = existing.filter(t => now - t < windowMs);
33
+ if (recent.length >= maxCalls) {
34
+ timestamps.set(toolName, recent);
35
+ return false;
36
+ }
37
+ recent.push(now);
38
+ timestamps.set(toolName, recent);
39
+ return true;
40
+ },
41
+ reset() {
42
+ timestamps.clear();
43
+ },
44
+ destroy() {
45
+ clearInterval(cleanupTimer);
46
+ timestamps.clear();
47
+ },
48
+ };
49
+ }
50
+ /**
51
+ * Wrap an McpServer so that each tool registered through the wrapper
52
+ * has per-tool rate limiting applied before the handler executes.
53
+ */
54
+ export function wrapServerWithRateLimit(server, limiter) {
55
+ const originalRegisterTool = server.registerTool.bind(server);
56
+ // Create a proxy that intercepts registerTool calls
57
+ const proxy = new Proxy(server, {
58
+ get(target, prop, receiver) {
59
+ if (prop === 'registerTool') {
60
+ return function registerToolWithRateLimit(name, config, handler) {
61
+ const wrappedHandler = async (...handlerArgs) => {
62
+ if (!limiter.check(name)) {
63
+ return error('RATE_LIMITED', `Too many requests for tool "${name}" — please wait before retrying.`, 'Wait 60 seconds before calling this tool again.');
64
+ }
65
+ return handler(...handlerArgs);
66
+ };
67
+ return originalRegisterTool(name, config, wrappedHandler);
68
+ };
69
+ }
70
+ return Reflect.get(target, prop, receiver);
71
+ },
72
+ });
73
+ return proxy;
74
+ }
75
+ //# sourceMappingURL=rate-limit.js.map