@goplausible/algorand-mcp 4.2.2 → 4.2.4

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
@@ -369,8 +369,8 @@ See [Secure Wallet](#secure-wallet) for full architecture details.
369
369
  | `wallet_remove_account` | Remove an account from the wallet by nickname or index |
370
370
  | `wallet_list_accounts` | List all accounts with nicknames, addresses, and limits |
371
371
  | `wallet_switch_account` | Switch the active account by nickname or index |
372
- | `wallet_get_info` | Get active account info: address, public key, balance, and spending limits |
373
- | `wallet_get_assets` | Get all asset holdings for the active account |
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`. |
373
+ | `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
374
  | `wallet_sign_transaction` | Sign a single transaction with the active account (enforces spending limits) |
375
375
  | `wallet_sign_transaction_group` | Sign a group of transactions with the active account (auto-assigns group ID) |
376
376
  | `wallet_sign_data` | Sign arbitrary hex data with raw Ed25519 (noble, no SDK prefix) |
@@ -437,11 +437,13 @@ See [Secure Wallet](#secure-wallet) for full architecture details.
437
437
  | `compile_teal` | Compile TEAL source code |
438
438
  | `disassemble_teal` | Disassemble TEAL bytecode to source |
439
439
  | `send_raw_transaction` | Submit signed transactions to the network |
440
- | `simulate_raw_transactions` | Simulate raw transactions |
441
- | `simulate_transactions` | Simulate transactions with detailed config |
440
+ | `simulate_raw_transactions` | Simulate already-encoded transactions (base64 bytes). Pass/fail + log/cost only — no trace, no extra budget. |
441
+ | `simulate_transactions` | Simulate decoded transaction groups with full `SimulateRequest` config (trace, extra opcode budget, unnamed-resource handling, unsigned txns). |
442
442
 
443
443
  ### Algod API Tools (13 tools)
444
444
 
445
+ Live, current-state reads against an Algod node. **Default choice for account/application/asset lookups** — the matching indexer endpoints were intentionally disabled to keep the tool surface lean (see [.notes/redundant-tools-report.md](.notes/redundant-tools-report.md)). Use the indexer family below only when you need historical or filtered queries that algod cannot serve.
446
+
445
447
  | Tool | Description |
446
448
  |---|---|
447
449
  | `api_algod_get_account_info` | Get account balance, assets, and auth address |
@@ -458,27 +460,24 @@ See [Secure Wallet](#secure-wallet) for full architecture details.
458
460
  | `api_algod_get_node_status` | Get current node status |
459
461
  | `api_algod_get_node_status_after_block` | Get node status after a specific round |
460
462
 
461
- ### Indexer API Tools (17 tools)
463
+ ### Indexer API Tools (10 tools)
464
+
465
+ Historical / filtered queries against an Algorand Indexer instance. Use these for time-range scans, paginated searches, log retrieval, and creator/holder discovery — anything algod's current-state endpoints cannot answer.
466
+
467
+ Seven indexer endpoints that duplicated algod equivalents (account-by-id, account assets, account app local states, application by id, application box, application boxes, asset by id) were intentionally disabled. They live commented-out in [src/tools/apiManager/indexer/](src/tools/apiManager/indexer/) and can be re-enabled in one place if needed.
462
468
 
463
469
  | Tool | Description |
464
470
  |---|---|
465
- | `api_indexer_lookup_account_by_id` | Get account information |
466
- | `api_indexer_lookup_account_assets` | Get account assets |
467
- | `api_indexer_lookup_account_app_local_states` | Get account app local states |
468
471
  | `api_indexer_lookup_account_created_applications` | Get apps created by account |
469
- | `api_indexer_search_for_accounts` | Search accounts with filters |
470
- | `api_indexer_lookup_applications` | Get application information |
471
- | `api_indexer_lookup_application_logs` | Get application log messages |
472
- | `api_indexer_search_for_applications` | Search applications |
473
- | `api_indexer_lookup_application_box` | Get application box by name |
474
- | `api_indexer_lookup_application_boxes` | Get all application boxes |
475
- | `api_indexer_lookup_asset_by_id` | Get asset info and configuration |
476
- | `api_indexer_lookup_asset_balances` | Get asset holders and balances |
477
- | `api_indexer_lookup_asset_transactions` | Get transactions for an asset |
478
- | `api_indexer_search_for_assets` | Search assets |
479
- | `api_indexer_lookup_transaction_by_id` | Get transaction by ID |
480
- | `api_indexer_lookup_account_transactions` | Get account transaction history |
481
- | `api_indexer_search_for_transactions` | Search transactions |
472
+ | `api_indexer_search_for_accounts` | Search accounts with filters (asset/app holdings, balance ranges) |
473
+ | `api_indexer_lookup_application_logs` | Get application log messages over a round range |
474
+ | `api_indexer_search_for_applications` | Search applications by creator |
475
+ | `api_indexer_lookup_asset_balances` | Get all accounts holding an asset with their balances |
476
+ | `api_indexer_lookup_asset_transactions` | Get transactions involving an asset (time/round/address-role filters) |
477
+ | `api_indexer_search_for_assets` | Search assets by creator, name, or unit |
478
+ | `api_indexer_lookup_transaction_by_id` | Get a confirmed transaction by ID |
479
+ | `api_indexer_lookup_account_transactions` | Get an account's transaction history (time/round/type/asset filters) |
480
+ | `api_indexer_search_for_transactions` | Search transactions across the chain with filters |
482
481
 
483
482
  ### NFDomains Tools (6 tools)
484
483
 
@@ -701,7 +700,7 @@ npm test
701
700
  | `transactionManager` | Payment, asset, app transaction building; sign_transaction; assign_group_id |
702
701
  | `algodManager` | TEAL compile/disassemble, send raw, simulate |
703
702
  | `apiAlgod` | All 13 algod API tools with correct mock routing |
704
- | `apiIndexer` | All 17 indexer API tools with fluent builder mocks |
703
+ | `apiIndexer` | All 10 active indexer API tools with fluent builder mocks |
705
704
  | `apiNfd` | NFD get/search/browse with mocked fetch |
706
705
  | `apiTinyman` | Tinyman pool/swap with error handling |
707
706
  | `arc26Manager` | ARC-26 URI generation and QR code SVG output |
@@ -246,12 +246,12 @@ AlgodManager.algodTools = [
246
246
  },
247
247
  {
248
248
  name: 'simulate_raw_transactions',
249
- description: 'Simulate raw transactions',
249
+ description: 'Simulate already-encoded, optionally-signed transactions (base64 bytes). Use this when you have raw txn bytes ready and only need a pass/fail + log/cost result — no execution tracing, no extra opcode budget, no unnamed-resource handling. For a richer simulation (trace, extra budget, unsigned txns, multiple groups), use simulate_transactions.',
250
250
  inputSchema: withCommonParams(algodToolSchemas.simulateRawTransactions),
251
251
  },
252
252
  {
253
253
  name: 'simulate_transactions',
254
- description: 'Simulate transactions with detailed configuration',
254
+ description: 'Simulate one or more transaction groups built from decoded transaction objects, with a full SimulateRequest config (allowEmptySignatures, allowMoreLogging, allowUnnamedResources, execTraceConfig, extraOpcodeBudget, round). Use this when you need execution traces, extra opcode budget, to simulate unsigned txns, or to opt into Algorand v9+ simulate features. For a quick pass/fail check on pre-encoded bytes, use simulate_raw_transactions instead.',
255
255
  inputSchema: withCommonParams(algodToolSchemas.simulateTransactions),
256
256
  }
257
257
  ];
@@ -38,7 +38,7 @@ export declare const apiManager: ({
38
38
  src: string;
39
39
  mimeType?: string | undefined;
40
40
  sizes?: string[] | undefined;
41
- theme?: "dark" | "light" | undefined;
41
+ theme?: "light" | "dark" | undefined;
42
42
  }[] | undefined;
43
43
  title?: string | undefined;
44
44
  })[];
@@ -3,60 +3,64 @@ import { getIndexerClient, extractNetwork } from '../../../algorand-client.js';
3
3
  import { ResponseProcessor } from '../../../utils/responseProcessor.js';
4
4
  import { withCommonParams } from '../../commonParams.js';
5
5
  export const accountTools = [
6
- {
7
- name: 'api_indexer_lookup_account_by_id',
8
- description: 'Get account information from indexer',
9
- inputSchema: withCommonParams({
10
- type: 'object',
11
- properties: {
12
- address: {
13
- type: 'string',
14
- description: 'Account address'
15
- }
16
- },
17
- required: ['address']
18
- })
19
- },
20
- {
21
- name: 'api_indexer_lookup_account_assets',
22
- description: 'Get account assets',
23
- inputSchema: withCommonParams({
24
- type: 'object',
25
- properties: {
26
- address: {
27
- type: 'string',
28
- description: 'Account address'
29
- },
30
- limit: {
31
- type: 'integer',
32
- description: 'Maximum number of assets to return'
33
- },
34
- assetId: {
35
- type: 'integer',
36
- description: 'Filter by asset ID'
37
- },
38
- nextToken: {
39
- type: 'string',
40
- description: 'Token for retrieving the next page of results'
41
- }
42
- },
43
- required: ['address']
44
- })
45
- },
46
- {
47
- name: 'api_indexer_lookup_account_app_local_states',
48
- description: 'Get account application local states',
49
- inputSchema: withCommonParams({
50
- type: 'object',
51
- properties: {
52
- address: {
53
- type: 'string',
54
- description: 'Account address'
55
- }
56
- },
57
- required: ['address']
58
- })
59
- },
6
+ // Disabled: redundant with api_algod_get_account_info (live algod variant covers the same query).
7
+ // See .notes/redundant-tools-report.md §1.
8
+ // {
9
+ // name: 'api_indexer_lookup_account_by_id',
10
+ // description: 'Get account information from indexer',
11
+ // inputSchema: withCommonParams({
12
+ // type: 'object',
13
+ // properties: {
14
+ // address: {
15
+ // type: 'string',
16
+ // description: 'Account address'
17
+ // }
18
+ // },
19
+ // required: ['address']
20
+ // })
21
+ // },
22
+ // Disabled: redundant with api_algod_get_account_asset_info.
23
+ // {
24
+ // name: 'api_indexer_lookup_account_assets',
25
+ // description: 'Get account assets',
26
+ // inputSchema: withCommonParams({
27
+ // type: 'object',
28
+ // properties: {
29
+ // address: {
30
+ // type: 'string',
31
+ // description: 'Account address'
32
+ // },
33
+ // limit: {
34
+ // type: 'integer',
35
+ // description: 'Maximum number of assets to return'
36
+ // },
37
+ // assetId: {
38
+ // type: 'integer',
39
+ // description: 'Filter by asset ID'
40
+ // },
41
+ // nextToken: {
42
+ // type: 'string',
43
+ // description: 'Token for retrieving the next page of results'
44
+ // }
45
+ // },
46
+ // required: ['address']
47
+ // })
48
+ // },
49
+ // Disabled: redundant with api_algod_get_account_application_info.
50
+ // {
51
+ // name: 'api_indexer_lookup_account_app_local_states',
52
+ // description: 'Get account application local states',
53
+ // inputSchema: withCommonParams({
54
+ // type: 'object',
55
+ // properties: {
56
+ // address: {
57
+ // type: 'string',
58
+ // description: 'Account address'
59
+ // }
60
+ // },
61
+ // required: ['address']
62
+ // })
63
+ // },
60
64
  {
61
65
  name: 'api_indexer_lookup_account_created_applications',
62
66
  description: 'Get applications created by this account',
@@ -218,16 +222,18 @@ export const handleAccountTools = ResponseProcessor.wrapResourceHandler(async fu
218
222
  const name = args.name;
219
223
  const network = extractNetwork(args);
220
224
  switch (name) {
221
- case 'api_indexer_lookup_account_by_id': {
222
- const { address } = args;
223
- const info = await lookupAccountByID(address, network);
224
- return info;
225
- }
226
- case 'api_indexer_lookup_account_app_local_states': {
227
- const { address } = args;
228
- const info = await lookupAccountAppLocalStates(address, network);
229
- return info;
230
- }
225
+ // Disabled: redundant with api_algod_get_account_info. See .notes/redundant-tools-report.md §1.
226
+ // case 'api_indexer_lookup_account_by_id': {
227
+ // const { address } = args;
228
+ // const info = await lookupAccountByID(address, network);
229
+ // return info;
230
+ // }
231
+ // Disabled: redundant with api_algod_get_account_application_info.
232
+ // case 'api_indexer_lookup_account_app_local_states': {
233
+ // const { address } = args;
234
+ // const info = await lookupAccountAppLocalStates(address, network);
235
+ // return info;
236
+ // }
231
237
  case 'api_indexer_lookup_account_created_applications': {
232
238
  const { address } = args;
233
239
  const info = await lookupAccountCreatedApplications(address, network);
@@ -237,11 +243,12 @@ export const handleAccountTools = ResponseProcessor.wrapResourceHandler(async fu
237
243
  const info = await searchAccounts(args, network);
238
244
  return info.accounts;
239
245
  }
240
- case 'api_indexer_lookup_account_assets': {
241
- const { address, ...params } = args;
242
- const info = await lookupAccountAssets(address, params, network);
243
- return info.assets;
244
- }
246
+ // Disabled: redundant with api_algod_get_account_asset_info.
247
+ // case 'api_indexer_lookup_account_assets': {
248
+ // const { address, ...params } = args;
249
+ // const info = await lookupAccountAssets(address, params, network);
250
+ // return info.assets;
251
+ // }
245
252
  default:
246
253
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
247
254
  }
@@ -4,20 +4,22 @@ import { ResponseProcessor } from '../../../utils/responseProcessor.js';
4
4
  import { withCommonParams } from '../../commonParams.js';
5
5
  import algosdk from 'algosdk';
6
6
  export const applicationTools = [
7
- {
8
- name: 'api_indexer_lookup_applications',
9
- description: 'Get application information from indexer',
10
- inputSchema: withCommonParams({
11
- type: 'object',
12
- properties: {
13
- appId: {
14
- type: 'integer',
15
- description: 'Application ID'
16
- }
17
- },
18
- required: ['appId']
19
- })
20
- },
7
+ // Disabled: redundant with api_algod_get_application_by_id (live algod variant covers the same query).
8
+ // See .notes/redundant-tools-report.md §1.
9
+ // {
10
+ // name: 'api_indexer_lookup_applications',
11
+ // description: 'Get application information from indexer',
12
+ // inputSchema: withCommonParams({
13
+ // type: 'object',
14
+ // properties: {
15
+ // appId: {
16
+ // type: 'integer',
17
+ // description: 'Application ID'
18
+ // }
19
+ // },
20
+ // required: ['appId']
21
+ // })
22
+ // },
21
23
  {
22
24
  name: 'api_indexer_lookup_application_logs',
23
25
  description: 'Get application log messages',
@@ -77,42 +79,44 @@ export const applicationTools = [
77
79
  }
78
80
  })
79
81
  },
80
- {
81
- name: 'api_indexer_lookup_application_box',
82
- description: 'Get application box by name',
83
- inputSchema: withCommonParams({
84
- type: 'object',
85
- properties: {
86
- appId: {
87
- type: 'integer',
88
- description: 'Application ID'
89
- },
90
- boxName: {
91
- type: 'string',
92
- description: 'Box name Buffer'
93
- }
94
- },
95
- required: ['appId', 'boxName']
96
- })
97
- },
98
- {
99
- name: 'api_indexer_lookup_application_boxes',
100
- description: 'Get all application boxes',
101
- inputSchema: withCommonParams({
102
- type: 'object',
103
- properties: {
104
- appId: {
105
- type: 'integer',
106
- description: 'Application ID'
107
- },
108
- maxBoxes: {
109
- type: 'integer',
110
- description: 'Maximum number of boxes to return'
111
- }
112
- },
113
- required: ['appId']
114
- })
115
- }
82
+ // Disabled: redundant with api_algod_get_application_box.
83
+ // {
84
+ // name: 'api_indexer_lookup_application_box',
85
+ // description: 'Get application box by name',
86
+ // inputSchema: withCommonParams({
87
+ // type: 'object',
88
+ // properties: {
89
+ // appId: {
90
+ // type: 'integer',
91
+ // description: 'Application ID'
92
+ // },
93
+ // boxName: {
94
+ // type: 'string',
95
+ // description: 'Box name Buffer'
96
+ // }
97
+ // },
98
+ // required: ['appId', 'boxName']
99
+ // })
100
+ // },
101
+ // Disabled: redundant with api_algod_get_application_boxes.
102
+ // {
103
+ // name: 'api_indexer_lookup_application_boxes',
104
+ // description: 'Get all application boxes',
105
+ // inputSchema: withCommonParams({
106
+ // type: 'object',
107
+ // properties: {
108
+ // appId: {
109
+ // type: 'integer',
110
+ // description: 'Application ID'
111
+ // },
112
+ // maxBoxes: {
113
+ // type: 'integer',
114
+ // description: 'Maximum number of boxes to return'
115
+ // }
116
+ // },
117
+ // required: ['appId']
118
+ // })
119
+ // }
116
120
  ];
117
121
  export async function lookupApplications(appId, network = 'mainnet') {
118
122
  try {
@@ -260,11 +264,12 @@ export const handleApplicationTools = ResponseProcessor.wrapResourceHandler(asyn
260
264
  const name = args.name;
261
265
  const network = extractNetwork(args);
262
266
  switch (name) {
263
- case 'api_indexer_lookup_applications': {
264
- const { appId } = args;
265
- const info = await lookupApplications(appId, network);
266
- return info.application;
267
- }
267
+ // Disabled: redundant with api_algod_get_application_by_id. See .notes/redundant-tools-report.md §1.
268
+ // case 'api_indexer_lookup_applications': {
269
+ // const { appId } = args;
270
+ // const info = await lookupApplications(appId, network);
271
+ // return info.application;
272
+ // }
268
273
  case 'api_indexer_lookup_application_logs': {
269
274
  const { appId, ...params } = args;
270
275
  const logs = await lookupApplicationLogs(appId, params, network);
@@ -274,16 +279,18 @@ export const handleApplicationTools = ResponseProcessor.wrapResourceHandler(asyn
274
279
  const info = await searchForApplications(args, network);
275
280
  return info.applications;
276
281
  }
277
- case 'api_indexer_lookup_application_box': {
278
- const { appId, boxName } = args;
279
- const box = await lookupApplicationBoxByIDandName(appId, boxName, network);
280
- return box;
281
- }
282
- case 'api_indexer_lookup_application_boxes': {
283
- const { appId, maxBoxes } = args;
284
- const boxes = await searchForApplicationBoxes(appId, maxBoxes, network);
285
- return boxes.boxes;
286
- }
282
+ // Disabled: redundant with api_algod_get_application_box.
283
+ // case 'api_indexer_lookup_application_box': {
284
+ // const { appId, boxName } = args;
285
+ // const box = await lookupApplicationBoxByIDandName(appId, boxName, network);
286
+ // return box;
287
+ // }
288
+ // Disabled: redundant with api_algod_get_application_boxes.
289
+ // case 'api_indexer_lookup_application_boxes': {
290
+ // const { appId, maxBoxes } = args;
291
+ // const boxes = await searchForApplicationBoxes(appId, maxBoxes, network);
292
+ // return boxes.boxes;
293
+ // }
287
294
  default:
288
295
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
289
296
  }
@@ -3,20 +3,22 @@ import { getIndexerClient, extractNetwork } from '../../../algorand-client.js';
3
3
  import { ResponseProcessor } from '../../../utils/responseProcessor.js';
4
4
  import { withCommonParams } from '../../commonParams.js';
5
5
  export const assetTools = [
6
- {
7
- name: 'api_indexer_lookup_asset_by_id',
8
- description: 'Get asset information and configuration',
9
- inputSchema: withCommonParams({
10
- type: 'object',
11
- properties: {
12
- assetId: {
13
- type: 'integer',
14
- description: 'Asset ID'
15
- }
16
- },
17
- required: ['assetId']
18
- })
19
- },
6
+ // Disabled: redundant with api_algod_get_asset_by_id (live algod variant covers the same query).
7
+ // See .notes/redundant-tools-report.md §1.
8
+ // {
9
+ // name: 'api_indexer_lookup_asset_by_id',
10
+ // description: 'Get asset information and configuration',
11
+ // inputSchema: withCommonParams({
12
+ // type: 'object',
13
+ // properties: {
14
+ // assetId: {
15
+ // type: 'integer',
16
+ // description: 'Asset ID'
17
+ // }
18
+ // },
19
+ // required: ['assetId']
20
+ // })
21
+ // },
20
22
  {
21
23
  name: 'api_indexer_lookup_asset_balances',
22
24
  description: 'Get accounts holding this asset and their balances',
@@ -267,11 +269,12 @@ export const handleAssetTools = ResponseProcessor.wrapResourceHandler(async func
267
269
  const name = args.name;
268
270
  const network = extractNetwork(args);
269
271
  switch (name) {
270
- case 'api_indexer_lookup_asset_by_id': {
271
- const { assetId } = args;
272
- const info = await lookupAssetByID(assetId, network);
273
- return info.asset;
274
- }
272
+ // Disabled: redundant with api_algod_get_asset_by_id. See .notes/redundant-tools-report.md §1.
273
+ // case 'api_indexer_lookup_asset_by_id': {
274
+ // const { assetId } = args;
275
+ // const info = await lookupAssetByID(assetId, network);
276
+ // return info.asset;
277
+ // }
275
278
  case 'api_indexer_lookup_asset_balances': {
276
279
  const { assetId, ...params } = args;
277
280
  const balances = await lookupAssetBalances(assetId, params, network);
@@ -17,11 +17,6 @@ export declare class Arc26Manager {
17
17
  * @param params The parameters for constructing the URI
18
18
  * @returns Object containing the URI and QR code as base64 data URL
19
19
  */
20
- generateUriAndQr(params: Arc26ToolInput): Promise<{
21
- uri: string;
22
- qrCodePng: string;
23
- qrCodeUtf8: string;
24
- }>;
25
20
  /**
26
21
  * Constructs an Algorand URI according to ARC-26 specification and generates a QR code
27
22
  * @param params The parameters for constructing the URI
@@ -1,4 +1,3 @@
1
- import * as QRCode from 'qrcode';
2
1
  import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
3
2
  import { withCommonParams } from './commonParams.js';
4
3
  export class Arc26Manager {
@@ -50,70 +49,70 @@ export class Arc26Manager {
50
49
  * @param params The parameters for constructing the URI
51
50
  * @returns Object containing the URI and QR code as base64 data URL
52
51
  */
53
- async generateUriAndQr(params) {
54
- // Validate address format (base32 string)
55
- if (!params.address || !/^[A-Z2-7]{58}$/.test(params.address)) {
56
- throw new McpError(ErrorCode.InvalidParams, 'Invalid Algorand address format');
57
- }
58
- // Start building the URI with the scheme and address
59
- let uri = `algorand://${params.address}`;
60
- // Build query parameters
61
- const queryParams = [];
62
- // Add optional parameters if provided
63
- if (params.label) {
64
- queryParams.push(`label=${encodeURIComponent(params.label)}`);
65
- }
66
- if (typeof params.amount === 'number') {
67
- if (params.amount < 0) {
68
- throw new McpError(ErrorCode.InvalidParams, 'Amount must be non-negative');
69
- }
70
- // Convert to microAlgos and ensure no decimals
71
- const microAlgos = Math.floor(params.amount);
72
- queryParams.push(`amount=${microAlgos}`);
73
- }
74
- if (typeof params.asset === 'number') {
75
- if (params.asset < 0) {
76
- throw new McpError(ErrorCode.InvalidParams, 'Asset ID must be non-negative');
77
- }
78
- queryParams.push(`asset=${params.asset}`);
79
- }
80
- if (params.note) {
81
- queryParams.push(`note=${encodeURIComponent(params.note)}`);
82
- }
83
- if (params.xnote) {
84
- queryParams.push(`xnote=${encodeURIComponent(params.xnote)}`);
85
- }
86
- // Add query parameters to URI if any exist
87
- if (queryParams.length > 0) {
88
- uri += '?' + queryParams.join('&');
89
- }
90
- // Generate QR code as SVG
91
- const qrCodeUtf8Raw = await QRCode.toString(uri, {
92
- type: 'utf8',
93
- errorCorrectionLevel: 'H',
94
- // margin: 1,
95
- width: 128
96
- });
97
- // Invert for better terminal contrast
98
- const qrCodeUtf8 = qrCodeUtf8Raw
99
- .replace(/█/g, '⬜').replace(/ /g, '█').replace(/⬜/g, ' ')
100
- .replace(/▀/g, '⬛').replace(/▄/g, '▀').replace(/⬛/g, '▄');
101
- const qrCodePng = await QRCode.toDataURL(uri, {
102
- type: 'image/png',
103
- errorCorrectionLevel: 'H',
104
- color: {
105
- dark: '#000000',
106
- light: '#FFFFFF'
107
- },
108
- margin: 4,
109
- width: 128
110
- });
111
- return {
112
- uri,
113
- qrCodePng: qrCodePng,
114
- qrCodeUtf8: qrCodeUtf8
115
- };
116
- }
52
+ // async generateUriAndQr(params: Arc26ToolInput): Promise<{ uri: string; qrCodePng: string; qrCodeUtf8: string }> {
53
+ // // Validate address format (base32 string)
54
+ // if (!params.address || !/^[A-Z2-7]{58}$/.test(params.address)) {
55
+ // throw new McpError(ErrorCode.InvalidParams, 'Invalid Algorand address format');
56
+ // }
57
+ // // Start building the URI with the scheme and address
58
+ // let uri = `algorand://${params.address}`;
59
+ // // Build query parameters
60
+ // const queryParams: string[] = [];
61
+ // // Add optional parameters if provided
62
+ // if (params.label) {
63
+ // queryParams.push(`label=${encodeURIComponent(params.label)}`);
64
+ // }
65
+ // if (typeof params.amount === 'number') {
66
+ // if (params.amount < 0) {
67
+ // throw new McpError(ErrorCode.InvalidParams, 'Amount must be non-negative');
68
+ // }
69
+ // // Convert to microAlgos and ensure no decimals
70
+ // const microAlgos = Math.floor(params.amount);
71
+ // queryParams.push(`amount=${microAlgos}`);
72
+ // }
73
+ // if (typeof params.asset === 'number') {
74
+ // if (params.asset < 0) {
75
+ // throw new McpError(ErrorCode.InvalidParams, 'Asset ID must be non-negative');
76
+ // }
77
+ // queryParams.push(`asset=${params.asset}`);
78
+ // }
79
+ // if (params.note) {
80
+ // queryParams.push(`note=${encodeURIComponent(params.note)}`);
81
+ // }
82
+ // if (params.xnote) {
83
+ // queryParams.push(`xnote=${encodeURIComponent(params.xnote)}`);
84
+ // }
85
+ // // Add query parameters to URI if any exist
86
+ // if (queryParams.length > 0) {
87
+ // uri += '?' + queryParams.join('&');
88
+ // }
89
+ // // Generate QR code as SVG
90
+ // const qrCodeUtf8Raw = await QRCode.toString(uri, {
91
+ // type: 'utf8',
92
+ // errorCorrectionLevel: 'H',
93
+ // // margin: 1,
94
+ // width: 128
95
+ // });
96
+ // // Invert for better terminal contrast
97
+ // const qrCodeUtf8 = qrCodeUtf8Raw
98
+ // .replace(/█/g, '⬜').replace(/ /g, '█').replace(/⬜/g, ' ')
99
+ // .replace(/▀/g, '⬛').replace(/▄/g, '▀').replace(/⬛/g, '▄');
100
+ // const qrCodePng = await QRCode.toDataURL(uri, {
101
+ // type: 'image/png',
102
+ // errorCorrectionLevel: 'H',
103
+ // color: {
104
+ // dark: '#000000',
105
+ // light: '#FFFFFF'
106
+ // },
107
+ // margin: 4,
108
+ // width: 128
109
+ // });
110
+ // return {
111
+ // uri,
112
+ // qrCodePng: qrCodePng,
113
+ // qrCodeUtf8: qrCodeUtf8
114
+ // };
115
+ // }
117
116
  /**
118
117
  * Constructs an Algorand URI according to ARC-26 specification and generates a QR code
119
118
  * @param params The parameters for constructing the URI
@@ -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.2',
89
+ version: '4.2.4',
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).',
@@ -67,7 +67,6 @@ const walletToolSchemas = {
67
67
  addAccount: {
68
68
  type: 'object',
69
69
  properties: {
70
- mnemonic: { type: 'string', description: '25-word mnemonic to import. If omitted, a new account is generated.' },
71
70
  nickname: { type: 'string', description: 'Human-readable nickname for this account' },
72
71
  allowance: { type: 'number', description: 'Max per-transaction amount in microAlgos (0 = unlimited)' },
73
72
  dailyAllowance: { type: 'number', description: 'Max daily spending total in microAlgos (0 = unlimited)' }
@@ -767,12 +766,12 @@ WalletManager.walletTools = [
767
766
  },
768
767
  {
769
768
  name: 'wallet_get_info',
770
- description: 'Get the active wallet account info including address, public key, nickname, on-chain balance, and spending limits.',
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.',
771
770
  inputSchema: withCommonParams(walletToolSchemas.getInfo),
772
771
  },
773
772
  {
774
773
  name: 'wallet_get_assets',
775
- description: 'Get all asset holdings for the active wallet account.',
774
+ description: 'Get all ASA holdings for the ACTIVE wallet account (an account whose private key this MCP server owns). Returns the wallet nickname, address, network, and the assets array. Use this when listing holdings of "the wallet you control." For asset holdings of an arbitrary on-chain account, use api_algod_get_account_info (returns full account including assets) or api_algod_get_account_asset_info (single asset).',
776
775
  inputSchema: withCommonParams(walletToolSchemas.getAssets),
777
776
  },
778
777
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goplausible/algorand-mcp",
3
- "version": "4.2.2",
3
+ "version": "4.2.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -13,6 +13,9 @@
13
13
  "test:e2e": "NODE_OPTIONS='--experimental-vm-modules' jest --config tests/jest.config.e2e.js",
14
14
  "test:all": "npm test && npm run test:e2e",
15
15
  "typecheck": "tsc --noEmit",
16
+ "tag": "git tag v$npm_package_version && git push origin v$npm_package_version",
17
+ "retag": "git tag -f v$npm_package_version && git push -f origin v$npm_package_version",
18
+ "publish:npm": "npm publish",
16
19
  "prepublishOnly": "npm run clean && npm run build"
17
20
  },
18
21
  "keywords": [