@goplausible/algorand-mcp 4.2.4 → 4.2.6

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 CHANGED
@@ -14,7 +14,7 @@ Algorand is a carbon-negative, pure proof-of-stake Layer 1 blockchain with insta
14
14
  ## Features
15
15
 
16
16
  - Secure wallet management via OS keychain — private keys never exposed to agents or LLMs
17
- - Wallet accounts nicknames, allowances, and daily limits for safe spending control
17
+ - Wallet accounts with human-readable nicknames
18
18
  - Account creation, key management, and rekeying
19
19
  - Transaction building, signing, and submission (payments, assets, applications, key registration)
20
20
  - Atomic transaction groups
@@ -276,7 +276,7 @@ The wallet system has two layers of storage, each with a distinct security role:
276
276
  | Layer | What it stores | Where | Encryption |
277
277
  |---|---|---|---|
278
278
  | **OS Keychain** | Mnemonics (secret keys) | macOS Keychain / Linux libsecret / Windows Credential Manager | OS-managed, hardware-backed where available |
279
- | **Embedded SQLite** | Account metadata (nicknames, allowances, spend tracking) | `~/.algorand-mcp/wallet.db` | Plaintext (no secrets) |
279
+ | **Embedded SQLite** | Account metadata (nicknames, active-account index) | `~/.algorand-mcp/wallet.db` | Plaintext (no secrets) |
280
280
 
281
281
  **Private key material never appears in tool responses, MCP config files, environment variables, or logs.** The agent only sees addresses, public keys, and signed transaction blobs.
282
282
 
@@ -290,31 +290,23 @@ The wallet system has two layers of storage, each with a distinct security role:
290
290
  │ { nickname: "main" } │ │
291
291
  │ ──────────────────────────► │ generate keypair │
292
292
  │ │ store mnemonic ──────────► │ OS Keychain (encrypted)
293
- │ │ store metadata ──────────► │ SQLite (nickname, limits)
293
+ │ │ store metadata ──────────► │ SQLite (nickname)
294
294
  │ ◄─ { address, publicKey } │ │
295
295
  │ │ │
296
296
  │ wallet_sign_transaction │ │
297
297
  │ { transaction: {...} } │ │
298
- │ ──────────────────────────► │ check spending limits
299
- │ │ retrieve mnemonic ◄────── │ OS Keychain
298
+ │ ──────────────────────────► │ retrieve mnemonic ◄────── OS Keychain
300
299
  │ │ sign in memory │
301
300
  │ ◄─ { txID, blob } │ (key discarded) │
302
301
  │ │ │
303
302
  ```
304
303
 
305
- 1. **Account creation** (`wallet_add_account`) — Generates a keypair, stores the mnemonic in the OS keychain, and stores metadata (nickname, spending limits) in SQLite. Returns **only** address and public key.
304
+ 1. **Account creation** (`wallet_add_account`) — Generates a keypair, stores the mnemonic in the OS keychain, and stores the nickname in SQLite. Returns **only** address and public key.
306
305
  2. **Active account** — One account is active at a time. `wallet_switch_account` changes it by nickname or index. All signing and query tools operate on the active account.
307
- 3. **Transaction signing** (`wallet_sign_transaction`) — Checks per-transaction and daily spending limits, retrieves the key from the keychain, signs in memory, discards the key. Returns only the signed blob.
306
+ 3. **Transaction signing** (`wallet_sign_transaction`) — Retrieves the key from the keychain, signs in memory, discards the key. Returns only the signed blob.
308
307
  4. **Data signing** (`wallet_sign_data`) — Signs arbitrary hex data using raw Ed25519 via the [`@noble/curves`](https://github.com/paulmillr/noble-curves) library (no Algorand SDK prefix). Useful for off-chain authentication.
309
308
  5. **Asset opt-in** (`wallet_optin_asset`) — Creates, signs, and submits an opt-in transaction for the active account in one step.
310
309
 
311
- ### Spending limits
312
-
313
- Each account has two configurable limits (in microAlgos, 0 = unlimited):
314
-
315
- - **`allowance`** — Maximum amount per single transaction. Rejects any transaction exceeding this.
316
- - **`dailyAllowance`** — Maximum total spend per calendar day across all transactions. Automatically resets at midnight. Tracked in SQLite.
317
-
318
310
  ### Platform keychain support
319
311
 
320
312
  The keychain backend is provided by [`@napi-rs/keyring`](https://github.com/nicolo-ribaudo/napi-keyring) (Rust-based, prebuilt binaries):
@@ -365,13 +357,13 @@ See [Secure Wallet](#secure-wallet) for full architecture details.
365
357
 
366
358
  | Tool | Description |
367
359
  |---|---|
368
- | `wallet_add_account` | Create a new Algorand account with nickname and spending limits (returns address + public key only) |
360
+ | `wallet_add_account` | Create a new Algorand account with nickname (returns address + public key only) |
369
361
  | `wallet_remove_account` | Remove an account from the wallet by nickname or index |
370
- | `wallet_list_accounts` | List all accounts with nicknames, addresses, and limits |
362
+ | `wallet_list_accounts` | List all accounts with nicknames and addresses |
371
363
  | `wallet_switch_account` | Switch the active account by nickname or index |
372
- | `wallet_get_info` | Get info for the **active account this MCP server owns** (keychain-backed): address, public key, balance, opted-in counts, plus wallet-only fields (allowance, daily allowance, daily spent). For arbitrary on-chain accounts use `api_algod_get_account_info`. |
364
+ | `wallet_get_info` | Get info for the **active account this MCP server owns** (keychain-backed): address, public key, balance, opted-in counts. For arbitrary on-chain accounts use `api_algod_get_account_info`. |
373
365
  | `wallet_get_assets` | Get all ASA holdings for the **active account this MCP server owns**. For arbitrary on-chain accounts use `api_algod_get_account_info` or `api_algod_get_account_asset_info`. |
374
- | `wallet_sign_transaction` | Sign a single transaction with the active account (enforces spending limits) |
366
+ | `wallet_sign_transaction` | Sign a single transaction with the active account |
375
367
  | `wallet_sign_transaction_group` | Sign a group of transactions with the active account (auto-assigns group ID) |
376
368
  | `wallet_sign_data` | Sign arbitrary hex data with raw Ed25519 (noble, no SDK prefix) |
377
369
  | `wallet_optin_asset` | Opt the active account into an asset (creates, signs, and submits) |
@@ -28,7 +28,7 @@ function getEndpoints(network) {
28
28
  // Memoization caches
29
29
  const algodClients = new Map();
30
30
  const indexerClients = new Map();
31
- export function getAlgodClient(network = 'mainnet') {
31
+ export function getAlgodClient(network = 'testnet') {
32
32
  const key = `${network}:${ALGORAND_TOKEN}`;
33
33
  let client = algodClients.get(key);
34
34
  if (!client) {
@@ -38,7 +38,7 @@ export function getAlgodClient(network = 'mainnet') {
38
38
  }
39
39
  return client;
40
40
  }
41
- export function getIndexerClient(network = 'mainnet') {
41
+ export function getIndexerClient(network = 'testnet') {
42
42
  const key = `${network}:${ALGORAND_TOKEN}`;
43
43
  let client = indexerClients.get(key);
44
44
  if (!client) {
@@ -53,5 +53,5 @@ export function extractNetwork(args) {
53
53
  if (network && !['mainnet', 'testnet', 'localnet'].includes(network)) {
54
54
  throw new Error(`Invalid network: ${network}. Must be mainnet, testnet, or localnet.`);
55
55
  }
56
- return (network || 'mainnet');
56
+ return (network || 'testnet');
57
57
  }
@@ -83,10 +83,7 @@ export async function handleSwapTools(args) {
83
83
  if (maxDepth !== undefined)
84
84
  quoteParams.maxDepth = maxDepth;
85
85
  const quote = await router.newQuote(quoteParams);
86
- // 4. Check spending limits (estimate based on input amount for ALGO sends)
87
- const estimatedSpend = fromASAID === 0 ? Number(amount) : 0;
88
- WalletManager.checkWalletSpendingLimits(account, estimatedSpend);
89
- // 5. Build and execute swap
86
+ // 4. Build and execute swap
90
87
  const swapConfig = {
91
88
  quote,
92
89
  address,
@@ -98,12 +95,10 @@ export async function handleSwapTools(args) {
98
95
  }
99
96
  const swap = await router.newSwap(swapConfig);
100
97
  const result = await swap.execute();
101
- // 6. Record spend
102
- await WalletManager.recordWalletSpend(address, estimatedSpend);
103
- // 7. Get swap summary
98
+ // 5. Get swap summary
104
99
  const summary = swap.getSummary();
105
100
  const inputTxnId = swap.getInputTransactionId();
106
- // 8. Build response
101
+ // 6. Build response
107
102
  const response = {
108
103
  status: 'confirmed',
109
104
  confirmedRound: result.confirmedRound.toString(),
@@ -109,7 +109,7 @@ export const accountTools = [
109
109
  })
110
110
  }
111
111
  ];
112
- export async function lookupAccountByID(address, network = 'mainnet') {
112
+ export async function lookupAccountByID(address, network = 'testnet') {
113
113
  try {
114
114
  const indexerClient = getIndexerClient(network);
115
115
  console.error(`Looking up account info for address ${address}`);
@@ -125,7 +125,7 @@ export async function lookupAccountByID(address, network = 'mainnet') {
125
125
  throw new McpError(ErrorCode.InternalError, `Failed to get account info: ${error instanceof Error ? error.message : String(error)}`);
126
126
  }
127
127
  }
128
- export async function lookupAccountAssets(address, params, network = 'mainnet') {
128
+ export async function lookupAccountAssets(address, params, network = 'testnet') {
129
129
  try {
130
130
  const indexerClient = getIndexerClient(network);
131
131
  console.error(`Looking up assets for address ${address}`);
@@ -151,7 +151,7 @@ export async function lookupAccountAssets(address, params, network = 'mainnet')
151
151
  throw new McpError(ErrorCode.InternalError, `Failed to get account assets: ${error instanceof Error ? error.message : String(error)}`);
152
152
  }
153
153
  }
154
- export async function lookupAccountAppLocalStates(address, network = 'mainnet') {
154
+ export async function lookupAccountAppLocalStates(address, network = 'testnet') {
155
155
  try {
156
156
  const indexerClient = getIndexerClient(network);
157
157
  console.error(`Looking up app local states for address ${address}`);
@@ -167,7 +167,7 @@ export async function lookupAccountAppLocalStates(address, network = 'mainnet')
167
167
  throw new McpError(ErrorCode.InternalError, `Failed to get account application local states: ${error instanceof Error ? error.message : String(error)}`);
168
168
  }
169
169
  }
170
- export async function lookupAccountCreatedApplications(address, network = 'mainnet') {
170
+ export async function lookupAccountCreatedApplications(address, network = 'testnet') {
171
171
  try {
172
172
  const indexerClient = getIndexerClient(network);
173
173
  console.error(`Looking up created applications for address ${address}`);
@@ -183,7 +183,7 @@ export async function lookupAccountCreatedApplications(address, network = 'mainn
183
183
  throw new McpError(ErrorCode.InternalError, `Failed to get account created applications: ${error instanceof Error ? error.message : String(error)}`);
184
184
  }
185
185
  }
186
- export async function searchAccounts(params, network = 'mainnet') {
186
+ export async function searchAccounts(params, network = 'testnet') {
187
187
  try {
188
188
  const indexerClient = getIndexerClient(network);
189
189
  console.error('Searching accounts with params:', params);
@@ -118,7 +118,7 @@ export const applicationTools = [
118
118
  // })
119
119
  // }
120
120
  ];
121
- export async function lookupApplications(appId, network = 'mainnet') {
121
+ export async function lookupApplications(appId, network = 'testnet') {
122
122
  try {
123
123
  const indexerClient = getIndexerClient(network);
124
124
  console.error(`Looking up application info for ID ${appId}`);
@@ -134,7 +134,7 @@ export async function lookupApplications(appId, network = 'mainnet') {
134
134
  throw new McpError(ErrorCode.InternalError, `Failed to get application info: ${error instanceof Error ? error.message : String(error)}`);
135
135
  }
136
136
  }
137
- export async function lookupApplicationLogs(appId, params, network = 'mainnet') {
137
+ export async function lookupApplicationLogs(appId, params, network = 'testnet') {
138
138
  try {
139
139
  const indexerClient = getIndexerClient(network);
140
140
  console.error(`Looking up logs for application ${appId}`);
@@ -169,7 +169,7 @@ export async function lookupApplicationLogs(appId, params, network = 'mainnet')
169
169
  throw new McpError(ErrorCode.InternalError, `Failed to get application logs: ${error instanceof Error ? error.message : String(error)}`);
170
170
  }
171
171
  }
172
- export async function searchForApplications(params, network = 'mainnet') {
172
+ export async function searchForApplications(params, network = 'testnet') {
173
173
  try {
174
174
  const indexerClient = getIndexerClient(network);
175
175
  console.error('Searching applications with params:', params);
@@ -195,7 +195,7 @@ export async function searchForApplications(params, network = 'mainnet') {
195
195
  throw new McpError(ErrorCode.InternalError, `Failed to search applications: ${error instanceof Error ? error.message : String(error)}`);
196
196
  }
197
197
  }
198
- export async function lookupApplicationBoxByIDandName(appId, boxName, network = 'mainnet') {
198
+ export async function lookupApplicationBoxByIDandName(appId, boxName, network = 'testnet') {
199
199
  try {
200
200
  const indexerClient = getIndexerClient(network);
201
201
  const encoder = new TextEncoder();
@@ -240,7 +240,7 @@ export async function lookupApplicationBoxByIDandName(appId, boxName, network =
240
240
  throw new McpError(ErrorCode.InternalError, `Failed to get application box: ${error instanceof Error ? error.message : String(error)}`);
241
241
  }
242
242
  }
243
- export async function searchForApplicationBoxes(appId, maxBoxes, network = 'mainnet') {
243
+ export async function searchForApplicationBoxes(appId, maxBoxes, network = 'testnet') {
244
244
  try {
245
245
  const indexerClient = getIndexerClient(network);
246
246
  console.error(`Searching boxes for application ${appId}`);
@@ -141,7 +141,7 @@ export const assetTools = [
141
141
  })
142
142
  }
143
143
  ];
144
- export async function lookupAssetByID(assetId, network = 'mainnet') {
144
+ export async function lookupAssetByID(assetId, network = 'testnet') {
145
145
  try {
146
146
  const indexerClient = getIndexerClient(network);
147
147
  console.error(`Looking up asset info for ID ${assetId}`);
@@ -157,7 +157,7 @@ export async function lookupAssetByID(assetId, network = 'mainnet') {
157
157
  throw new McpError(ErrorCode.InternalError, `Failed to get asset info: ${error instanceof Error ? error.message : String(error)}`);
158
158
  }
159
159
  }
160
- export async function lookupAssetBalances(assetId, params, network = 'mainnet') {
160
+ export async function lookupAssetBalances(assetId, params, network = 'testnet') {
161
161
  try {
162
162
  const indexerClient = getIndexerClient(network);
163
163
  console.error(`Looking up balances for asset ${assetId}`);
@@ -186,7 +186,7 @@ export async function lookupAssetBalances(assetId, params, network = 'mainnet')
186
186
  throw new McpError(ErrorCode.InternalError, `Failed to get asset balances: ${error instanceof Error ? error.message : String(error)}`);
187
187
  }
188
188
  }
189
- export async function lookupAssetTransactions(assetId, params, network = 'mainnet') {
189
+ export async function lookupAssetTransactions(assetId, params, network = 'testnet') {
190
190
  try {
191
191
  const indexerClient = getIndexerClient(network);
192
192
  console.error(`Looking up transactions for asset ${assetId}`);
@@ -230,7 +230,7 @@ export async function lookupAssetTransactions(assetId, params, network = 'mainne
230
230
  throw new McpError(ErrorCode.InternalError, `Failed to get asset transactions: ${error instanceof Error ? error.message : String(error)}`);
231
231
  }
232
232
  }
233
- export async function searchForAssets(params, network = 'mainnet') {
233
+ export async function searchForAssets(params, network = 'testnet') {
234
234
  try {
235
235
  const indexerClient = getIndexerClient(network);
236
236
  console.error('Searching assets with params:', params);
@@ -125,7 +125,7 @@ export const transactionTools = [
125
125
  })
126
126
  }
127
127
  ];
128
- export async function lookupTransactionByID(txId, network = 'mainnet') {
128
+ export async function lookupTransactionByID(txId, network = 'testnet') {
129
129
  try {
130
130
  const indexerClient = getIndexerClient(network);
131
131
  console.error(`Looking up transaction with ID ${txId}`);
@@ -145,7 +145,7 @@ export async function lookupTransactionByID(txId, network = 'mainnet') {
145
145
  throw new McpError(ErrorCode.InternalError, `Failed to get transaction: ${error instanceof Error ? error.message : String(error)}`);
146
146
  }
147
147
  }
148
- export async function lookupAccountTransactions(address, params, network = 'mainnet') {
148
+ export async function lookupAccountTransactions(address, params, network = 'testnet') {
149
149
  try {
150
150
  const indexerClient = getIndexerClient(network);
151
151
  console.error(`Looking up transactions for account ${address}`);
@@ -188,7 +188,7 @@ export async function lookupAccountTransactions(address, params, network = 'main
188
188
  throw new McpError(ErrorCode.InternalError, `Failed to get account transactions: ${error instanceof Error ? error.message : String(error)}`);
189
189
  }
190
190
  }
191
- export async function searchForTransactions(params, network = 'mainnet') {
191
+ export async function searchForTransactions(params, network = 'testnet') {
192
192
  try {
193
193
  const indexerClient = getIndexerClient(network);
194
194
  console.error('Searching transactions with params:', params);
@@ -3,7 +3,7 @@ export const networkParam = {
3
3
  network: {
4
4
  type: 'string',
5
5
  enum: ['mainnet', 'testnet', 'localnet'],
6
- description: 'Algorand network to use (default: mainnet)',
6
+ description: 'Algorand network to use (default: testnet)',
7
7
  },
8
8
  };
9
9
  export const itemsPerPageParam = {
@@ -86,7 +86,7 @@ export class UtilityManager {
86
86
  type: 'text',
87
87
  text: JSON.stringify({
88
88
  name: 'Algorand MCP Server',
89
- version: '4.2.4',
89
+ version: '4.2.6',
90
90
  builder: 'GoPlausible',
91
91
  description: 'A Model Context Protocol (MCP) server providing comprehensive access to the Algorand blockchain. Supports account management, transaction building and signing, smart contract interaction, asset operations, ARC-26 URI generation, and deep integration with Algorand ecosystem services including NFDomains, Tinyman, Vestige, and Ultrade.',
92
92
  blockchain: 'Algorand — a carbon-negative, pure proof-of-stake Layer 1 blockchain delivering instant finality, low fees, and advanced smart contract capabilities via AVM (Algorand Virtual Machine).',
@@ -3,10 +3,6 @@ export interface AccountRow {
3
3
  address: string;
4
4
  public_key: string;
5
5
  nickname: string;
6
- allowance: number;
7
- daily_allowance: number;
8
- daily_spent: number;
9
- last_spend_date: string;
10
6
  created_at: string;
11
7
  }
12
8
  export declare class WalletManager {
@@ -24,15 +20,12 @@ export declare class WalletManager {
24
20
  private static getMnemonic;
25
21
  private static deleteMnemonic;
26
22
  private static getActiveSecretKey;
27
- private static checkSpendingLimits;
28
- private static recordSpend;
29
23
  /**
30
24
  * Converts flat JSON transaction format (as returned by make_*_txn / assign_group_id)
31
25
  * into an algosdk v3 Transaction object. Handles genesisHash in base64 string,
32
26
  * Uint8Array, or JSON-round-tripped {0:byte,...} object form.
33
27
  */
34
28
  private static prepareTransaction;
35
- private static extractTxnAmount;
36
29
  static handleTool(name: string, args: Record<string, unknown>): Promise<{
37
30
  content: {
38
31
  type: string;
@@ -42,12 +35,8 @@ export declare class WalletManager {
42
35
  static getAccounts(): Promise<AccountRow[]>;
43
36
  static hasAccounts(): Promise<boolean>;
44
37
  static getActiveAccountInfo(): Promise<AccountRow | null>;
45
- /** Get active account details (address, nickname, spending limits). */
38
+ /** Get active account details (address, nickname). */
46
39
  static getActiveWalletAccount(): Promise<AccountRow>;
47
40
  /** Get active account's secret key from OS keychain. */
48
41
  static getActiveWalletSecretKey(): Promise<Uint8Array>;
49
- /** Check if transaction amount is within spending limits. Throws on violation. */
50
- static checkWalletSpendingLimits(account: AccountRow, amountMicroAlgos: number): void;
51
- /** Record a spend against the account's daily allowance tracker. */
52
- static recordWalletSpend(address: string, amountMicroAlgos: number): Promise<void>;
53
42
  }
@@ -35,10 +35,6 @@ async function getDb() {
35
35
  address TEXT UNIQUE NOT NULL,
36
36
  public_key TEXT NOT NULL,
37
37
  nickname TEXT UNIQUE NOT NULL,
38
- allowance INTEGER DEFAULT 0,
39
- daily_allowance INTEGER DEFAULT 0,
40
- daily_spent INTEGER DEFAULT 0,
41
- last_spend_date TEXT DEFAULT '',
42
38
  created_at TEXT NOT NULL
43
39
  )
44
40
  `);
@@ -67,9 +63,7 @@ const walletToolSchemas = {
67
63
  addAccount: {
68
64
  type: 'object',
69
65
  properties: {
70
- nickname: { type: 'string', description: 'Human-readable nickname for this account' },
71
- allowance: { type: 'number', description: 'Max per-transaction amount in microAlgos (0 = unlimited)' },
72
- dailyAllowance: { type: 'number', description: 'Max daily spending total in microAlgos (0 = unlimited)' }
66
+ nickname: { type: 'string', description: 'Human-readable nickname for this account' }
73
67
  },
74
68
  required: ['nickname']
75
69
  },
@@ -221,35 +215,6 @@ export class WalletManager {
221
215
  const mnemonic = WalletManager.getMnemonic(account.address);
222
216
  return algosdk.mnemonicToSecretKey(mnemonic).sk;
223
217
  }
224
- // ── Spending Limit Enforcement ─────────────────────────────────────────────
225
- static checkSpendingLimits(account, amountMicroAlgos) {
226
- if (amountMicroAlgos <= 0)
227
- return;
228
- if (account.allowance > 0 && amountMicroAlgos > account.allowance) {
229
- throw new McpError(ErrorCode.InvalidRequest, `Transaction amount ${amountMicroAlgos} microAlgos exceeds per-transaction allowance of ${account.allowance} for account "${account.nickname}"`);
230
- }
231
- if (account.daily_allowance > 0) {
232
- const today = new Date().toISOString().split('T')[0];
233
- const spent = account.last_spend_date === today ? account.daily_spent : 0;
234
- if (spent + amountMicroAlgos > account.daily_allowance) {
235
- throw new McpError(ErrorCode.InvalidRequest, `Would exceed daily allowance: ${spent} spent + ${amountMicroAlgos} = ${spent + amountMicroAlgos} > limit ${account.daily_allowance} for "${account.nickname}"`);
236
- }
237
- }
238
- }
239
- static async recordSpend(address, amountMicroAlgos) {
240
- if (amountMicroAlgos <= 0)
241
- return;
242
- const database = await getDb();
243
- const today = new Date().toISOString().split('T')[0];
244
- // Reset if new day, then add
245
- database.run(`
246
- UPDATE accounts SET
247
- daily_spent = CASE WHEN last_spend_date = ? THEN daily_spent + ? ELSE ? END,
248
- last_spend_date = ?
249
- WHERE address = ?
250
- `, [today, amountMicroAlgos, amountMicroAlgos, today, address]);
251
- persistDb();
252
- }
253
218
  // ── Transaction Helpers ────────────────────────────────────────────────────
254
219
  /**
255
220
  * Converts flat JSON transaction format (as returned by make_*_txn / assign_group_id)
@@ -384,13 +349,6 @@ export class WalletManager {
384
349
  }
385
350
  return txn;
386
351
  }
387
- static extractTxnAmount(txnObj) {
388
- if (typeof txnObj.amount === 'number')
389
- return txnObj.amount;
390
- if (typeof txnObj.amt === 'number')
391
- return txnObj.amt;
392
- return 0;
393
- }
394
352
  // ── Tool Handler ───────────────────────────────────────────────────────────
395
353
  static async handleTool(name, args) {
396
354
  try {
@@ -420,9 +378,7 @@ export class WalletManager {
420
378
  }
421
379
  // Store mnemonic in OS keychain
422
380
  WalletManager.storeMnemonic(address, mnemonic);
423
- const allowance = typeof args.allowance === 'number' ? args.allowance : 0;
424
- const dailyAllowance = typeof args.dailyAllowance === 'number' ? args.dailyAllowance : 0;
425
- database.run('INSERT INTO accounts (address, public_key, nickname, allowance, daily_allowance, daily_spent, last_spend_date, created_at) VALUES (?, ?, ?, ?, ?, 0, \'\', ?)', [address, publicKey, args.nickname, allowance, dailyAllowance, new Date().toISOString()]);
381
+ database.run('INSERT INTO accounts (address, public_key, nickname, created_at) VALUES (?, ?, ?, ?)', [address, publicKey, args.nickname, new Date().toISOString()]);
426
382
  persistDb();
427
383
  const accounts = await WalletManager.getAllAccounts();
428
384
  const newIndex = accounts.findIndex(a => a.address === address);
@@ -438,8 +394,6 @@ export class WalletManager {
438
394
  publicKey,
439
395
  nickname: args.nickname,
440
396
  index: newIndex,
441
- allowance,
442
- dailyAllowance,
443
397
  }, null, 2),
444
398
  }],
445
399
  };
@@ -480,7 +434,6 @@ export class WalletManager {
480
434
  case 'wallet_list_accounts': {
481
435
  const accounts = await WalletManager.getAllAccounts();
482
436
  const activeIdx = await WalletManager.getActiveIndex();
483
- const today = new Date().toISOString().split('T')[0];
484
437
  return {
485
438
  content: [{
486
439
  type: 'text',
@@ -493,9 +446,6 @@ export class WalletManager {
493
446
  nickname: a.nickname,
494
447
  address: a.address,
495
448
  publicKey: a.public_key,
496
- allowance: a.allowance,
497
- dailyAllowance: a.daily_allowance,
498
- dailySpent: a.last_spend_date === today ? a.daily_spent : 0,
499
449
  createdAt: a.created_at,
500
450
  })),
501
451
  }, null, 2),
@@ -529,7 +479,6 @@ export class WalletManager {
529
479
  const account = await WalletManager.getActiveAccount();
530
480
  const network = extractNetwork(args);
531
481
  const algodClient = getAlgodClient(network);
532
- const today = new Date().toISOString().split('T')[0];
533
482
  let onChainInfo = {};
534
483
  try {
535
484
  const info = await algodClient.accountInformation(account.address).do();
@@ -554,9 +503,6 @@ export class WalletManager {
554
503
  publicKey: account.public_key,
555
504
  network,
556
505
  ...onChainInfo,
557
- allowance: account.allowance,
558
- dailyAllowance: account.daily_allowance,
559
- dailySpent: account.last_spend_date === today ? account.daily_spent : 0,
560
506
  }, null, 2),
561
507
  }],
562
508
  };
@@ -591,12 +537,9 @@ export class WalletManager {
591
537
  }
592
538
  const account = await WalletManager.getActiveAccount();
593
539
  const txnObj = args.transaction;
594
- const amount = WalletManager.extractTxnAmount(txnObj);
595
- WalletManager.checkSpendingLimits(account, amount);
596
540
  const txn = WalletManager.prepareTransaction(txnObj);
597
541
  const sk = await WalletManager.getActiveSecretKey();
598
542
  const signedTxn = algosdk.signTransaction(txn, sk);
599
- await WalletManager.recordSpend(account.address, amount);
600
543
  return {
601
544
  content: [{
602
545
  type: 'text',
@@ -615,11 +558,6 @@ export class WalletManager {
615
558
  throw new McpError(ErrorCode.InvalidParams, 'Non-empty transactions array is required');
616
559
  }
617
560
  const account = await WalletManager.getActiveAccount();
618
- let totalAmount = 0;
619
- for (const txnObj of args.transactions) {
620
- totalAmount += WalletManager.extractTxnAmount(txnObj);
621
- }
622
- WalletManager.checkSpendingLimits(account, totalAmount);
623
561
  const txns = args.transactions.map((txnObj) => WalletManager.prepareTransaction(txnObj));
624
562
  const groupedTxns = algosdk.assignGroupID(txns);
625
563
  const sk = await WalletManager.getActiveSecretKey();
@@ -630,7 +568,6 @@ export class WalletManager {
630
568
  blob: algosdk.bytesToBase64(signed.blob),
631
569
  };
632
570
  });
633
- await WalletManager.recordSpend(account.address, totalAmount);
634
571
  return {
635
572
  content: [{
636
573
  type: 'text',
@@ -726,7 +663,7 @@ export class WalletManager {
726
663
  }
727
664
  }
728
665
  // ── Public accessors for external tool integration (e.g., haystack-router) ──
729
- /** Get active account details (address, nickname, spending limits). */
666
+ /** Get active account details (address, nickname). */
730
667
  static async getActiveWalletAccount() {
731
668
  return WalletManager.getActiveAccount();
732
669
  }
@@ -734,19 +671,11 @@ export class WalletManager {
734
671
  static async getActiveWalletSecretKey() {
735
672
  return WalletManager.getActiveSecretKey();
736
673
  }
737
- /** Check if transaction amount is within spending limits. Throws on violation. */
738
- static checkWalletSpendingLimits(account, amountMicroAlgos) {
739
- return WalletManager.checkSpendingLimits(account, amountMicroAlgos);
740
- }
741
- /** Record a spend against the account's daily allowance tracker. */
742
- static async recordWalletSpend(address, amountMicroAlgos) {
743
- return WalletManager.recordSpend(address, amountMicroAlgos);
744
- }
745
674
  }
746
675
  WalletManager.walletTools = [
747
676
  {
748
677
  name: 'wallet_add_account',
749
- description: 'Create a new Algorand account and store it securely in the OS keychain with a nickname and spending limits. Returns only address and public key.',
678
+ description: 'Create a new Algorand account and store it securely in the OS keychain with a nickname. Returns only address and public key.',
750
679
  inputSchema: withCommonParams(walletToolSchemas.addAccount),
751
680
  },
752
681
  {
@@ -756,7 +685,7 @@ WalletManager.walletTools = [
756
685
  },
757
686
  {
758
687
  name: 'wallet_list_accounts',
759
- description: 'List all wallet accounts with their nicknames, addresses, and spending limits.',
688
+ description: 'List all wallet accounts with their nicknames and addresses.',
760
689
  inputSchema: withCommonParams(walletToolSchemas.listAccounts),
761
690
  },
762
691
  {
@@ -766,7 +695,7 @@ WalletManager.walletTools = [
766
695
  },
767
696
  {
768
697
  name: 'wallet_get_info',
769
- description: 'Get info for the ACTIVE wallet account (an account whose private key this MCP server owns in the OS keychain). Returns nickname, address, public key, network, on-chain balance, min-balance, opted-in apps/assets counts, plus wallet-only fields: per-tx allowance, daily allowance, and daily-spent counter. Use this when you want to know the state of "the wallet you control." For an arbitrary on-chain account that this wallet does NOT own, use api_algod_get_account_info instead.',
698
+ description: 'Get info for the ACTIVE wallet account (an account whose private key this MCP server owns in the OS keychain). Returns nickname, address, public key, network, on-chain balance, min-balance, opted-in apps/assets counts. Use this when you want to know the state of "the wallet you control." For an arbitrary on-chain account that this wallet does NOT own, use api_algod_get_account_info instead.',
770
699
  inputSchema: withCommonParams(walletToolSchemas.getInfo),
771
700
  },
772
701
  {
@@ -776,12 +705,12 @@ WalletManager.walletTools = [
776
705
  },
777
706
  {
778
707
  name: 'wallet_sign_transaction',
779
- description: 'Sign a single transaction with the active wallet account. Enforces per-transaction and daily spending limits.',
708
+ description: 'Sign a single transaction with the active wallet account.',
780
709
  inputSchema: withCommonParams(walletToolSchemas.signTransaction),
781
710
  },
782
711
  {
783
712
  name: 'wallet_sign_transaction_group',
784
- description: 'Sign a group of transactions with the active wallet account. Assigns group ID automatically and enforces spending limits.',
713
+ description: 'Sign a group of transactions with the active wallet account. Assigns group ID automatically.',
785
714
  inputSchema: withCommonParams(walletToolSchemas.signTransactionGroup),
786
715
  },
787
716
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goplausible/algorand-mcp",
3
- "version": "4.2.4",
3
+ "version": "4.2.6",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },