@goplausible/openclaw-algorand-plugin 1.7.2 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,16 +4,21 @@
4
4
 
5
5
  ## Features
6
6
 
7
- - **Local MCP Server**: Bundled `algorand-mcp` (99 tools) — wallet, transactions, smart contracts, TEAL, indexer, DEX, NFD, knowledge base
7
+ - **Local MCP Server**: Bundled `algorand-mcp` (107 tools) — wallet, transactions, smart contracts, TEAL, indexer, DEX, NFD, Haystack Router, Alpha Arcade, knowledge base
8
8
  - **x402 Payment Protocol**: Built-in `x402_fetch` tool for HTTP-native payments on Algorand
9
+ - **Headless Linux Keyring Persistence**: Automatic setup for wallet key persistence on cloud VMs and Docker (survives reboots)
9
10
  - **Interactive Setup**: Guided wizard for configuration
10
- - **Skills Included** (6 skills):
11
+ - **Skills Included** (9 skills):
11
12
  - `algorand-development` — AlgoKit CLI, project creation, general workflows
12
13
  - `algorand-typescript` — TypeScript smart contracts (PuyaTs)
13
14
  - `algorand-python` — Python smart contracts (PuyaPy)
14
15
  - `algorand-interaction` — Blockchain interaction via MCP (wallet, transactions, swaps, NFD)
15
16
  - `algorand-x402-typescript` — x402 payments in TypeScript
16
17
  - `algorand-x402-python` — x402 payments in Python
18
+ - `algorand-x402-payment` — Runtime x402 payment (agent as client)
19
+ - `haystack-router-development` — DEX aggregator SDK integration
20
+ - `haystack-router-interaction` — Best-price swaps via MCP tools
21
+ - `alpha-arcade-interaction` — Prediction markets interaction
17
22
 
18
23
  ## Installation
19
24
 
@@ -35,7 +40,7 @@ After installing, run these commands:
35
40
  # 1. Initialize plugin (write memory file + configure mcporter)
36
41
  openclaw algorand-plugin init
37
42
 
38
- # 2. Run interactive setup (configure x402 toggle)
43
+ # 2. Run interactive setup (keyring persistence + x402 toggle)
39
44
  openclaw algorand-plugin setup
40
45
 
41
46
  # 3. Restart the gateway
@@ -55,7 +60,7 @@ openclaw algorand-plugin mcp-config # Show MCP config snippet for external codi
55
60
 
56
61
  The plugin bundles [`@goplausible/algorand-mcp`](https://www.npmjs.com/package/@goplausible/algorand-mcp) as an npm dependency. It runs locally via stdio through [mcporter](https://www.npmjs.com/package/mcporter).
57
62
 
58
- - **99 tools** across 11 categories (wallet, transactions, algod, indexer, NFD, Tinyman, TEAL, knowledge base, and more)
63
+ - **107 tools** across 13 categories (wallet, transactions, algod, indexer, NFD, Tinyman, Haystack Router, Pera verification, Alpha Arcade, TEAL, knowledge base, and more)
59
64
  - **Multi-network**: `mainnet`, `testnet`, `localnet`
60
65
  - **Secure wallet**: Per-transaction and daily spending limits, private keys never exposed to agents
61
66
 
@@ -67,6 +72,17 @@ When `enableX402` is enabled (default), the plugin registers the `x402_fetch` to
67
72
  - Agent builds payment using algorand-mcp wallet tools (atomic group with facilitator-sponsored fees)
68
73
  - Agent retries with signed `PAYMENT-SIGNATURE` header to complete the payment and access the resource
69
74
 
75
+ ## Headless Linux (Cloud VMs, Docker)
76
+
77
+ On headless Linux, the OS keyring defaults to in-memory storage — wallet keys created by `algorand-mcp` are **lost on reboot**. The setup wizard (`openclaw algorand-plugin setup`) automatically detects this and:
78
+
79
+ 1. Installs `gnome-keyring`, `libsecret-tools`, `dbus-user-session`
80
+ 2. Enables `loginctl linger` for D-Bus session persistence
81
+ 3. Creates a persistent login keyring (auto-unlocked, no password)
82
+ 4. Backs up and restores existing wallet mnemonics if upgrading
83
+
84
+ After setup, wallet keys persist across reboots with no user interaction needed. Agent wallets are hot wallets — keep funds minimal and use QR code top-ups.
85
+
70
86
  ## Configuration
71
87
 
72
88
  Config is stored in `~/.openclaw/openclaw.json` under `plugins.entries.openclaw-algorand-plugin.config`:
package/index.ts CHANGED
@@ -301,6 +301,29 @@ export default function register(api: PluginApi) {
301
301
  console.log(" Config:");
302
302
  console.log(` x402: ${pluginConfig.enableX402 !== false ? "Enabled" : "Disabled"}`);
303
303
  console.log("");
304
+
305
+ // Keyring status
306
+ try {
307
+ const scriptPath = join(__dirname, "scripts", "setup-keyring.sh");
308
+ const keyringOutput = execSync(`bash "${scriptPath}" --detect`, { encoding: "utf-8", timeout: 5000 });
309
+ const vars = Object.fromEntries(
310
+ keyringOutput.trim().split("\n").map((line: string) => line.split("=", 2))
311
+ );
312
+ console.log(" Keyring:");
313
+ if (vars.PERSISTENT === "true") {
314
+ console.log(` Storage: ✅ ${vars.BACKEND}`);
315
+ console.log(` Wallets: ${vars.WALLET_DB_COUNT} account(s) in wallet.db`);
316
+ } else {
317
+ console.log(` Storage: ⚠️ ${vars.BACKEND} — run \`openclaw algorand-plugin setup\``);
318
+ if (parseInt(vars.WALLET_DB_COUNT) > 0) {
319
+ console.log(` Wallets: ⚠️ ${vars.WALLET_DB_COUNT} account(s) with mnemonics in volatile storage!`);
320
+ }
321
+ }
322
+ } catch {
323
+ console.log(" Keyring:");
324
+ console.log(" Storage: ❓ Could not detect");
325
+ }
326
+ console.log("");
304
327
  console.log(" Links:");
305
328
  console.log(` GoPlausible: ${GOPLAUSIBLE_SERVICES.website}`);
306
329
  console.log(` Algorand x402: ${GOPLAUSIBLE_SERVICES.x402}`);
@@ -346,6 +369,16 @@ export default function register(api: PluginApi) {
346
369
  console.log(" This plugin provides:");
347
370
  console.log(" • 9 Algorand skills (Algorand development in TS and Python, x402, MCP interaction, alpha arcade interaction, haystack router development and interaction)");
348
371
  console.log(" • algorand-mcp server (~100 blockchain tools via mcporter)\n");
372
+ // Keyring persistence warning for headless Linux
373
+ try {
374
+ const scriptPath = join(__dirname, "scripts", "setup-keyring.sh");
375
+ const keyringOutput = execSync(`bash "${scriptPath}" --detect`, { encoding: "utf-8", timeout: 5000 });
376
+ if (keyringOutput.includes("PERSISTENT=false")) {
377
+ console.log(" ⚠️ Headless Linux detected — wallet keys use in-memory storage.");
378
+ console.log(" Run `openclaw algorand-plugin setup` for persistent storage.\n");
379
+ }
380
+ } catch { /* ignore on install */ }
381
+
349
382
  console.log(" Next steps:");
350
383
  console.log(" 1. Run `openclaw algorand-plugin init` — configure mcporter + add plugin memory");
351
384
  console.log(" 2. Run `openclaw algorand-plugin setup` — configure options & add to allow list");
@@ -1,7 +1,7 @@
1
1
  export const ALGORAND_MCP = {
2
2
  id: "algorand-mcp",
3
3
  name: "Algorand MCP",
4
- description: "Local Algorand MCP server — 99 tools for blockchain interaction",
4
+ description: "Local Algorand MCP server — 107 tools for blockchain interaction",
5
5
  type: "stdio" as const,
6
6
  command: "algorand-mcp",
7
7
  } as const;
@@ -1,10 +1,12 @@
1
1
  # Algorand Plugin Guide
2
2
 
3
- This plugin enables three core capabilities:
3
+ This plugin enables four core capabilities:
4
4
 
5
5
  1. **Algorand Development** — Smart contracts, typed clients, React frontends via AlgoKit CLI and skills
6
- 2. **Blockchain Interaction** — Algorand MCP server (99 tools) via mcporter
6
+ 2. **Blockchain Interaction** — Algorand MCP server (107 tools) via mcporter (includes Pera asset verification)
7
7
  3. **x402 Payment Protocol** — HTTP-native payments with Algorand as first-class chain
8
+ 4. **Haystack Router** — DEX aggregator/smart order routing on Algorand (Tinyman V2, Pact, Folks)
9
+ 5. **Alpha Arcade** — On-chain prediction markets on Algorand (USDC-denominated, binary/multi-choice)
8
10
 
9
11
  ## Skill Routing
10
12
 
@@ -16,6 +18,10 @@ This plugin enables three core capabilities:
16
18
  | Interaction | Blockchain interaction via MCP | `algorand-interaction` |
17
19
  | x402 | TypeScript x402 development | `algorand-x402-typescript` |
18
20
  | x402 | Python x402 development | `algorand-x402-python` |
21
+ | x402 | Runtime x402 payment (Claude as client) | `algorand-x402-payment` |
22
+ | Haystack | Build swap apps with SDK | `haystack-router-development` |
23
+ | Haystack | Execute swaps via MCP tools | `haystack-router-interaction` |
24
+ | Alpha Arcade | Prediction markets via MCP tools | `alpha-arcade-interaction` |
19
25
 
20
26
  ## Using Algorand MCP Tools
21
27
 
@@ -31,7 +37,7 @@ mcporter call algorand-mcp.get_account_info address=XXXXX network=testnet
31
37
  mcporter call algorand-mcp.search_assets name=USDC network=mainnet
32
38
  ```
33
39
 
34
- ## MCP Tool Categories (101 tools)
40
+ ## MCP Tool Categories (107 tools)
35
41
 
36
42
  - **Wallet** (10) — `wallet_add_account`, `wallet_remove_account`, `wallet_list_accounts`, `wallet_switch_account`, `wallet_get_info`, `wallet_get_assets`, `wallet_sign_transaction`, `wallet_sign_transaction_group`, `wallet_sign_data`, `wallet_optin_asset`
37
43
  - **Account Management** (8) — `create_account`, `rekey_account`, `mnemonic_to_mdk`, `mdk_to_mnemonic`, `secret_key_to_mnemonic`, `mnemonic_to_secret_key`, `seed_from_mnemonic`, `mnemonic_from_seed`
@@ -42,9 +48,37 @@ mcporter call algorand-mcp.search_assets name=USDC network=mainnet
42
48
  - **Indexer API** (17) — `api_indexer_lookup_account_by_id`, `api_indexer_lookup_account_assets`, `api_indexer_lookup_account_app_local_states`, `api_indexer_lookup_account_created_applications`, `api_indexer_search_for_accounts`, `api_indexer_lookup_applications`, `api_indexer_lookup_application_logs`, `api_indexer_search_for_applications`, `api_indexer_lookup_application_box`, `api_indexer_lookup_application_boxes`, `api_indexer_lookup_asset_by_id`, `api_indexer_lookup_asset_balances`, `api_indexer_lookup_asset_transactions`, `api_indexer_search_for_assets`, `api_indexer_lookup_transaction_by_id`, `api_indexer_lookup_account_transactions`, `api_indexer_search_for_transactions`
43
49
  - **NFDomains** (6) — `api_nfd_get_nfd`, `api_nfd_get_nfds_for_addresses`, `api_nfd_get_nfd_activity`, `api_nfd_get_nfd_analytics`, `api_nfd_browse_nfds`, `api_nfd_search_nfds`
44
50
  - **Tinyman AMM** (9) — `api_tinyman_get_pool`, `api_tinyman_get_pool_analytics`, `api_tinyman_get_pool_creation_quote`, `api_tinyman_get_liquidity_quote`, `api_tinyman_get_remove_liquidity_quote`, `api_tinyman_get_swap_quote`, `api_tinyman_get_asset_optin_quote`, `api_tinyman_get_validator_optin_quote`, `api_tinyman_get_validator_optout_quote`
45
- - **ARC-26 URI** (1) — `generate_algorand_uri`
51
+ - **Haystack Router** (3) — `api_haystack_get_swap_quote`, `api_haystack_execute_swap`, `api_haystack_needs_optin`
52
+ - **Pera Asset Verification** (3) — `api_pera_asset_verification_status`, `api_pera_verified_asset_details`, `api_pera_verified_asset_search`
53
+ - **Alpha Arcade** (15) — Read: `alpha_get_live_markets`, `alpha_get_reward_markets`, `alpha_get_market`, `alpha_get_orderbook`, `alpha_get_open_orders`, `alpha_get_positions`. Trade: `alpha_create_limit_order`, `alpha_create_market_order`, `alpha_cancel_order`, `alpha_amend_order`, `alpha_propose_match`, `alpha_split_shares`, `alpha_merge_shares`, `alpha_claim`
54
+ - **ARC-26 URI** (1) — `generate_algorand_qrcode`
46
55
  - **Knowledge** (1) — `get_knowledge_doc` (categories: `arcs`, `sdks`, `algokit`, `algokit-utils`, `tealscript`, `puya`, `liquid-auth`, `python`, `developers`, `clis`, `nodes`, `details`)
47
56
 
57
+ ## QR Code Display (ARC-26 URI)
58
+
59
+ When generating QR codes with `generate_algorand_qrcode`, the tool returns:
60
+ - UTF-8 text QR code (terminal-friendly)
61
+ - PNG image as base64 (web-friendly)
62
+ - URI string
63
+
64
+ **Important:** MCP tool output may not render properly through mcporter → exec pipeline.
65
+ After calling the tool, **extract and paste the QR code directly in your response**:
66
+
67
+ 1. Call the tool and capture output
68
+ 2. Extract the UTF-8 QR block (Unicode block characters)
69
+ 3. Extract the base64 PNG data
70
+ 4. Include both in your reply:
71
+
72
+ ```
73
+ [paste UTF-8 QR here]
74
+ ```
75
+
76
+ ![QR Code](data:image/png;base64,[paste base64 here])
77
+
78
+ URI: `algorand://ADDRESS?amount=X&asset=Y`
79
+
80
+ This ensures the QR renders correctly in both terminal and web interfaces.
81
+
48
82
  ## Key things to remember
49
83
 
50
84
  - Always check wallet with `wallet_get_info` before blockchain operations
@@ -70,6 +104,7 @@ mcporter call algorand-mcp.search_assets name=USDC network=mainnet
70
104
  - **NEVER use PyTEAL or Beaker** — these are legacy. Use Algorand TypeScript or Algorand Python.
71
105
  - **NEVER use AlgoExplorer** — obsolete. Use Allo.info for block/account/transaction data.
72
106
  - **NFD (.algo names)**: Always use `depositAccount` field for transactions.
107
+ - **Alpha Arcade prices are microunits**: `yesProb`/`noProb` range 0–1,000,000 (NOT percentages). $0.50 = 500,000. Orders require both ALGO (MBR) and USDC (collateral).
73
108
 
74
109
  ## External resources
75
110
 
@@ -81,9 +116,12 @@ mcporter call algorand-mcp.search_assets name=USDC network=mainnet
81
116
  - Testnet Faucet: https://lora.algokit.io/testnet/fund
82
117
  - Testnet USDC Faucet: https://faucet.circle.com/
83
118
  - Algorand Developer Docs: https://dev.algorand.co/
84
- - Algorand Developer Docs Github : https://github.com/algorandfoundation/devportal
85
- - Algorand Developer Examples Github : https://github.com/algorandfoundation/devportal-code-examples
86
- - GoPlausible x402-avm Documentation and Example code : https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/README.md
87
- - GoPlausible x402-avm Examples template Projects : https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/
88
- - CAIP-2 Specification : https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md
89
- - Coinbase x402 Protocol : https://github.com/coinbase/x402
119
+ - Algorand Developer Docs Github: https://github.com/algorandfoundation/devportal
120
+ - Algorand Developer Examples Github: https://github.com/algorandfoundation/devportal-code-examples
121
+ - GoPlausible x402-avm Documentation and Example code: https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/README.md
122
+ - GoPlausible x402-avm Examples template Projects: https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/
123
+ - CAIP-2 Specification: https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md
124
+ - Coinbase x402 Protocol: https://github.com/coinbase/x402
125
+ - Haystack Router: https://github.com/TxnLab/haystack-router
126
+ - Alpha Arcade: https://alphaarcade.com
127
+ - Alpha Arcade API: https://platform.alphaarcade.com
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-algorand-plugin",
3
3
  "name": "Algorand Integration",
4
4
  "description": "Algorand blockchain integration with MCP and skills — by GoPlausible",
5
- "version": "1.7.2",
5
+ "version": "1.8.1",
6
6
  "skills": [
7
7
  "skills/algorand-development",
8
8
  "skills/algorand-typescript",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goplausible/openclaw-algorand-plugin",
3
- "version": "1.7.2",
3
+ "version": "1.8.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -27,6 +27,7 @@
27
27
  "index.ts",
28
28
  "setup.ts",
29
29
  "lib/",
30
+ "scripts/",
30
31
  "skills/",
31
32
  "memory/",
32
33
  "openclaw.plugin.json"
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ // Usage:
3
+ // node backup-keyring.js backup <output-file> — dump address\tmnemonic pairs
4
+ // node backup-keyring.js restore <input-file> — restore mnemonics from backup
5
+ // node backup-keyring.js count — print number of accounts in wallet.db
6
+ // node backup-keyring.js verify — check which accounts have mnemonics in keyring
7
+
8
+ const { Entry } = require('@napi-rs/keyring');
9
+ const initSqlJs = require('sql.js');
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const os = require('os');
13
+
14
+ const KEYCHAIN_SERVICE = 'algorand-mcp';
15
+ const WALLET_DB = path.join(os.homedir(), '.algorand-mcp', 'wallet.db');
16
+
17
+ async function getAccounts() {
18
+ if (!fs.existsSync(WALLET_DB)) return [];
19
+ const SQL = await initSqlJs();
20
+ const buf = fs.readFileSync(WALLET_DB);
21
+ const db = new SQL.Database(buf);
22
+ const rows = db.exec('SELECT address FROM accounts');
23
+ db.close();
24
+ if (!rows.length || !rows[0].values.length) return [];
25
+ return rows[0].values.map(r => r[0]);
26
+ }
27
+
28
+ function getMnemonic(address) {
29
+ try {
30
+ const entry = new Entry(KEYCHAIN_SERVICE, address);
31
+ const pw = entry.getPassword();
32
+ return pw || null;
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+
38
+ function setMnemonic(address, mnemonic) {
39
+ const entry = new Entry(KEYCHAIN_SERVICE, address);
40
+ entry.setPassword(mnemonic);
41
+ }
42
+
43
+ async function main() {
44
+ const mode = process.argv[2];
45
+ const file = process.argv[3];
46
+
47
+ if (mode === 'count') {
48
+ const accounts = await getAccounts();
49
+ console.log(accounts.length);
50
+ return;
51
+ }
52
+
53
+ if (mode === 'verify') {
54
+ const accounts = await getAccounts();
55
+ let ok = 0, missing = 0;
56
+ for (const addr of accounts) {
57
+ if (getMnemonic(addr)) {
58
+ console.log('OK ' + addr);
59
+ ok++;
60
+ } else {
61
+ console.log('MISSING ' + addr);
62
+ missing++;
63
+ }
64
+ }
65
+ console.log(`TOTAL=${accounts.length} OK=${ok} MISSING=${missing}`);
66
+ return;
67
+ }
68
+
69
+ if (mode === 'backup') {
70
+ if (!file) { console.error('Usage: backup-keyring.js backup <output-file>'); process.exit(1); }
71
+ const accounts = await getAccounts();
72
+ if (accounts.length === 0) {
73
+ console.log('NO_ACCOUNTS');
74
+ return;
75
+ }
76
+ let backed = 0, failed = 0;
77
+ const lines = [];
78
+ for (const addr of accounts) {
79
+ const mnemonic = getMnemonic(addr);
80
+ if (mnemonic) {
81
+ lines.push(addr + '\t' + mnemonic);
82
+ backed++;
83
+ } else {
84
+ console.error('MISSING ' + addr);
85
+ failed++;
86
+ }
87
+ }
88
+ if (lines.length > 0) {
89
+ fs.writeFileSync(file, lines.join('\n') + '\n', { mode: 0o600 });
90
+ }
91
+ console.log(`BACKED_UP=${backed} FAILED=${failed}`);
92
+ return;
93
+ }
94
+
95
+ if (mode === 'restore') {
96
+ if (!file) { console.error('Usage: backup-keyring.js restore <input-file>'); process.exit(1); }
97
+ if (!fs.existsSync(file)) { console.error('File not found: ' + file); process.exit(1); }
98
+ const content = fs.readFileSync(file, 'utf-8').trim();
99
+ if (!content) { console.log('RESTORED=0'); return; }
100
+ let restored = 0;
101
+ for (const line of content.split('\n')) {
102
+ const [addr, ...rest] = line.split('\t');
103
+ const mnemonic = rest.join('\t');
104
+ if (addr && mnemonic) {
105
+ setMnemonic(addr, mnemonic);
106
+ restored++;
107
+ }
108
+ }
109
+ console.log(`RESTORED=${restored}`);
110
+ return;
111
+ }
112
+
113
+ console.error('Usage: backup-keyring.js <backup|restore|count|verify> [file]');
114
+ process.exit(1);
115
+ }
116
+
117
+ main().catch(err => { console.error(err.message); process.exit(1); });
@@ -0,0 +1,428 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+
4
+ MODE="${1:---detect}"
5
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
6
+ PLUGIN_DIR="$(dirname "$SCRIPT_DIR")"
7
+ NODE_MODULES="$PLUGIN_DIR/node_modules"
8
+ BACKUP_SCRIPT="$SCRIPT_DIR/backup-keyring.js"
9
+ WALLET_DB="$HOME/.algorand-mcp/wallet.db"
10
+
11
+ # ═══════════════════════════════════════════════════════════
12
+ # 1. OS Detection
13
+ # ═══════════════════════════════════════════════════════════
14
+ if [[ "$(uname -s)" != "Linux" ]]; then
15
+ case "$(uname -s)" in
16
+ Darwin) BACKEND="macOS Keychain" ;;
17
+ *) BACKEND="OS Keychain" ;;
18
+ esac
19
+ if [ "$MODE" = "--detect" ]; then
20
+ echo "PLATFORM=$(uname -s | tr '[:upper:]' '[:lower:]')"
21
+ echo "BACKEND=$BACKEND"
22
+ echo "PERSISTENT=true"
23
+ echo "HEADLESS=false"
24
+ else
25
+ echo ""
26
+ echo " ✅ $BACKEND — persistent by default. No setup needed."
27
+ echo ""
28
+ fi
29
+ exit 0
30
+ fi
31
+
32
+ # ═══════════════════════════════════════════════════════════
33
+ # 2. Linux Environment Detection
34
+ # ═══════════════════════════════════════════════════════════
35
+
36
+ PLATFORM="linux"
37
+ KEYRING_DIR="$HOME/.local/share/keyrings"
38
+
39
+ # Display (headless?)
40
+ HAS_DISPLAY=false
41
+ [[ -n "${DISPLAY:-}" || -n "${WAYLAND_DISPLAY:-}" ]] && HAS_DISPLAY=true
42
+
43
+ # D-Bus session
44
+ HAS_DBUS=false
45
+ [[ -n "${DBUS_SESSION_BUS_ADDRESS:-}" ]] && HAS_DBUS=true
46
+
47
+ # GNOME Keyring daemon running
48
+ KEYRING_RUNNING=false
49
+ pgrep -u "$USER" gnome-keyring-daemon >/dev/null 2>&1 && KEYRING_RUNNING=true
50
+
51
+ # Keyring files on disk
52
+ KEYRING_FILES=false
53
+ [[ -d "$KEYRING_DIR" && "$(ls -A "$KEYRING_DIR" 2>/dev/null)" ]] && KEYRING_FILES=true
54
+
55
+ # Wallet DB exists and account count (via Node.js)
56
+ WALLET_DB_EXISTS=false
57
+ WALLET_DB_COUNT=0
58
+ [[ -f "$WALLET_DB" ]] && WALLET_DB_EXISTS=true
59
+ if [[ "$WALLET_DB_EXISTS" == "true" && -f "$BACKUP_SCRIPT" ]]; then
60
+ WALLET_DB_COUNT=$(NODE_PATH="$NODE_MODULES" node "$BACKUP_SCRIPT" count 2>/dev/null || echo "0")
61
+ fi
62
+
63
+ # Package manager
64
+ PKG=""
65
+ if command -v apt >/dev/null 2>&1; then
66
+ PKG="apt"
67
+ elif command -v dnf >/dev/null 2>&1; then
68
+ PKG="dnf"
69
+ elif command -v yum >/dev/null 2>&1; then
70
+ PKG="yum"
71
+ elif command -v pacman >/dev/null 2>&1; then
72
+ PKG="pacman"
73
+ elif command -v apk >/dev/null 2>&1; then
74
+ PKG="apk"
75
+ fi
76
+
77
+ # Container detection
78
+ IN_CONTAINER=false
79
+ [[ -f /.dockerenv ]] && IN_CONTAINER=true
80
+ grep -q 'docker\|lxc\|containerd' /proc/1/cgroup 2>/dev/null && IN_CONTAINER=true
81
+
82
+ # Persistence verdict
83
+ HEADLESS=false
84
+ if [[ "$HAS_DBUS" == "true" && "$KEYRING_RUNNING" == "true" && "$KEYRING_FILES" == "true" ]]; then
85
+ PERSISTENT="true"
86
+ BACKEND="GNOME Keyring (persistent)"
87
+ elif [[ "$HAS_DISPLAY" == "true" ]]; then
88
+ PERSISTENT="true"
89
+ BACKEND="Desktop Keyring (persistent)"
90
+ else
91
+ PERSISTENT="false"
92
+ BACKEND="In-memory (volatile)"
93
+ HEADLESS=true
94
+ fi
95
+
96
+ # ═══════════════════════════════════════════════════════════
97
+ # 3. Detect-only mode
98
+ # ═══════════════════════════════════════════════════════════
99
+ if [ "$MODE" = "--detect" ]; then
100
+ echo "PLATFORM=$PLATFORM"
101
+ echo "BACKEND=$BACKEND"
102
+ echo "PERSISTENT=$PERSISTENT"
103
+ echo "HEADLESS=$HEADLESS"
104
+ echo "HAS_DISPLAY=$HAS_DISPLAY"
105
+ echo "HAS_DBUS=$HAS_DBUS"
106
+ echo "KEYRING_RUNNING=$KEYRING_RUNNING"
107
+ echo "KEYRING_FILES=$KEYRING_FILES"
108
+ echo "WALLET_DB_EXISTS=$WALLET_DB_EXISTS"
109
+ echo "WALLET_DB_COUNT=$WALLET_DB_COUNT"
110
+ echo "PKG_MANAGER=$PKG"
111
+ echo "IN_CONTAINER=$IN_CONTAINER"
112
+ exit 0
113
+ fi
114
+
115
+ # ═══════════════════════════════════════════════════════════
116
+ # 4. Setup mode
117
+ # ═══════════════════════════════════════════════════════════
118
+
119
+ echo ""
120
+ echo " ── Algorand MCP Keyring Persistence Setup ──"
121
+ echo ""
122
+
123
+ # ─── 4a. Status display ───
124
+ echo " Platform: Linux"
125
+ echo " Display: $( [[ "$HAS_DISPLAY" == "true" ]] && echo "✅ Yes" || echo "❌ No (headless)" )"
126
+ echo " D-Bus session: $( [[ "$HAS_DBUS" == "true" ]] && echo "✅ Active" || echo "❌ Not found" )"
127
+ echo " Keyring daemon: $( [[ "$KEYRING_RUNNING" == "true" ]] && echo "✅ Running" || echo "❌ Not running" )"
128
+ echo " Keyring files: $( [[ "$KEYRING_FILES" == "true" ]] && echo "✅ Found in $KEYRING_DIR" || echo "❌ None (in-memory only)" )"
129
+ echo " Wallet DB: $( [[ "$WALLET_DB_EXISTS" == "true" ]] && echo "✅ $WALLET_DB ($WALLET_DB_COUNT account(s))" || echo "— Not found (fresh install)" )"
130
+ echo " Package manager: ${PKG:-unknown}"
131
+ echo " Container: $( [[ "$IN_CONTAINER" == "true" ]] && echo "Yes" || echo "No" )"
132
+ echo ""
133
+
134
+ # Already persistent with keyring files on disk?
135
+ if [[ "$PERSISTENT" == "true" && "$KEYRING_FILES" == "true" ]]; then
136
+ echo " ✅ Keyring is persistent and will survive reboots."
137
+ echo ""
138
+ exit 0
139
+ fi
140
+
141
+ # ─── 4b. Backup existing wallet mnemonics (UPDATE scenario only) ───
142
+ BACKUP_FILE=""
143
+ if [[ "$WALLET_DB_COUNT" -gt 0 && "$KEYRING_RUNNING" == "true" && -f "$BACKUP_SCRIPT" ]]; then
144
+ echo " ── Step 1: Backup wallet mnemonics from current keyring ──"
145
+ echo ""
146
+ echo " Found $WALLET_DB_COUNT account(s) in wallet.db."
147
+ echo " Reading mnemonics from current keyring before setup..."
148
+ echo ""
149
+
150
+ BACKUP_FILE=$(mktemp /tmp/algorand-mcp-keyring-backup.XXXXXX)
151
+ chmod 600 "$BACKUP_FILE"
152
+
153
+ BACKUP_RESULT=$(NODE_PATH="$NODE_MODULES" node "$BACKUP_SCRIPT" backup "$BACKUP_FILE" 2>&1)
154
+ echo " $BACKUP_RESULT"
155
+
156
+ # Check if backup actually has content
157
+ if [[ ! -s "$BACKUP_FILE" ]]; then
158
+ rm -f "$BACKUP_FILE"
159
+ BACKUP_FILE=""
160
+ echo " ℹ️ No mnemonics to backup (keyring may be empty after a reboot)."
161
+ fi
162
+ echo ""
163
+ elif [[ "$WALLET_DB_COUNT" -gt 0 && "$KEYRING_RUNNING" != "true" ]]; then
164
+ echo " ── Step 1: Backup ──"
165
+ echo ""
166
+ echo " ⚠️ Keyring daemon not running — cannot backup existing mnemonics."
167
+ echo " If the system was rebooted, in-memory mnemonics are already lost."
168
+ echo ""
169
+ fi
170
+
171
+ # ─── 4c. Install keyring packages ───
172
+ echo " ── Step 2: Install keyring packages ──"
173
+ echo ""
174
+
175
+ if [[ -z "$PKG" ]]; then
176
+ echo " Could not detect package manager. Install manually:"
177
+ echo " gnome-keyring, libsecret-tools, dbus-user-session (or equivalent)"
178
+ echo ""
179
+ else
180
+ case "$PKG" in
181
+ apt)
182
+ INSTALL_CMD="sudo DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt update && sudo DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt install -y gnome-keyring libsecret-tools dbus-user-session"
183
+ ;;
184
+ dnf)
185
+ INSTALL_CMD="sudo dnf install -y gnome-keyring libsecret libsecret-tools"
186
+ ;;
187
+ yum)
188
+ INSTALL_CMD="sudo yum install -y gnome-keyring libsecret libsecret-tools"
189
+ ;;
190
+ pacman)
191
+ INSTALL_CMD="sudo pacman -Sy --noconfirm gnome-keyring libsecret"
192
+ ;;
193
+ apk)
194
+ INSTALL_CMD="sudo apk add gnome-keyring libsecret dbus secret-tool"
195
+ ;;
196
+ esac
197
+
198
+ echo " Run:"
199
+ echo " $INSTALL_CMD"
200
+ echo ""
201
+ read -r -p " Install now? [y/N] " REPLY
202
+ if [[ "$REPLY" =~ ^[Yy]$ ]]; then
203
+ echo ""
204
+ eval "$INSTALL_CMD"
205
+ echo ""
206
+ echo " ✅ Packages installed."
207
+ else
208
+ echo " Skipped. Install later with the command above."
209
+ fi
210
+ echo ""
211
+ fi
212
+
213
+ # ─── 4d. Enable lingering user session ───
214
+ echo " ── Step 3: Enable user session lingering ──"
215
+ echo ""
216
+ echo " This keeps D-Bus and keyring daemon alive after SSH logout."
217
+ echo ""
218
+
219
+ if loginctl show-user "$USER" 2>/dev/null | grep -q "Linger=yes"; then
220
+ echo " ✅ Lingering already enabled for $USER"
221
+ else
222
+ read -r -p " Enable lingering for $USER? [y/N] " REPLY
223
+ if [[ "$REPLY" =~ ^[Yy]$ ]]; then
224
+ loginctl enable-linger "$USER"
225
+ echo " ✅ Lingering enabled."
226
+ else
227
+ echo " Skipped."
228
+ fi
229
+ fi
230
+ echo ""
231
+
232
+ # ─── 4e. Start D-Bus and keyring daemon ───
233
+ echo " ── Step 4: Start D-Bus session and keyring daemon ──"
234
+ echo ""
235
+
236
+ # Ensure D-Bus is available
237
+ if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then
238
+ echo " Starting D-Bus session..."
239
+ eval $(dbus-launch --sh-syntax)
240
+ echo " ✅ D-Bus session started."
241
+ else
242
+ echo " ✅ D-Bus session already active."
243
+ fi
244
+
245
+ # Kill stale keyring daemons (leftover --unlock processes, etc.)
246
+ STALE_COUNT=$(pgrep -u "$USER" gnome-keyring-daemon 2>/dev/null | wc -l)
247
+ if [[ "$STALE_COUNT" -gt 1 ]]; then
248
+ echo " Cleaning up $STALE_COUNT stale keyring daemon processes..."
249
+ pkill -9 -u "$USER" gnome-keyring-daemon 2>/dev/null || true
250
+ sleep 2
251
+ fi
252
+
253
+ # Start the daemon via --start (D-Bus activated, stays resident)
254
+ # Note: on headless, the daemon may be D-Bus activated on demand rather than
255
+ # staying resident. This is normal — secret-tool and @napi-rs/keyring will
256
+ # trigger D-Bus activation automatically when they query the Secret Service.
257
+ echo " Starting gnome-keyring-daemon..."
258
+ eval $(gnome-keyring-daemon --start --components=secrets 2>&1 | grep -v '^\*\*') || true
259
+ sleep 1
260
+
261
+ if pgrep -u "$USER" gnome-keyring-daemon >/dev/null 2>&1; then
262
+ echo " ✅ Keyring daemon running."
263
+ else
264
+ # On D-Bus activated systems, the daemon starts on demand — verify by querying
265
+ if command -v secret-tool >/dev/null 2>&1; then
266
+ secret-tool search --all service algorand-mcp 2>/dev/null && echo " ✅ Keyring daemon available (D-Bus activated)." || true
267
+ fi
268
+ echo " ℹ️ Daemon is D-Bus activated (starts on demand when queried)."
269
+ fi
270
+ echo ""
271
+
272
+ # ─── 4f. Create persistent login keyring collection ───
273
+ KEYRING_FILE="$KEYRING_DIR/login.keyring"
274
+ mkdir -p "$KEYRING_DIR"
275
+
276
+ echo " ── Step 5: Create persistent keyring ──"
277
+ echo ""
278
+
279
+ if [[ -f "$KEYRING_FILE" ]]; then
280
+ echo " ✅ Keyring already exists at $KEYRING_FILE"
281
+ else
282
+ # On headless Linux, gnome-keyring-daemon refuses to create a collection
283
+ # via normal prompts (no GUI). We use the D-Bus internal interface
284
+ # CreateWithMasterPassword to create the "login" collection with an
285
+ # empty master password — making it auto-unlocked and persistent on disk.
286
+ echo " Creating login keyring collection via D-Bus..."
287
+
288
+ python3 << 'PYEOF'
289
+ import sys
290
+ try:
291
+ from jeepney import new_method_call, DBusAddress
292
+ from jeepney.io.blocking import open_dbus_connection
293
+
294
+ conn = open_dbus_connection(bus='SESSION')
295
+
296
+ # Open a plain-text session with the Secret Service
297
+ msg = new_method_call(
298
+ DBusAddress('/org/freedesktop/secrets',
299
+ bus_name='org.freedesktop.secrets',
300
+ interface='org.freedesktop.Secret.Service'),
301
+ 'OpenSession',
302
+ 'sv',
303
+ ('plain', ('s', ''))
304
+ )
305
+ reply = conn.send_and_get_reply(msg)
306
+ session_path = reply.body[1]
307
+
308
+ # Create the "login" collection with empty master password
309
+ # Uses the internal gnome-keyring interface (bypasses GUI prompt)
310
+ props = {
311
+ 'org.freedesktop.Secret.Collection.Label': ('s', 'login')
312
+ }
313
+ master_password = (session_path, b'', b'', 'text/plain')
314
+
315
+ msg = new_method_call(
316
+ DBusAddress('/org/freedesktop/secrets',
317
+ bus_name='org.gnome.keyring',
318
+ interface='org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface'),
319
+ 'CreateWithMasterPassword',
320
+ 'a{sv}(oayays)',
321
+ (props, master_password)
322
+ )
323
+ reply = conn.send_and_get_reply(msg)
324
+ print(f" Collection created: {reply.body[0]}")
325
+ conn.close()
326
+ except Exception as e:
327
+ print(f" Error: {e}", file=sys.stderr)
328
+ sys.exit(1)
329
+ PYEOF
330
+
331
+ if [[ $? -eq 0 && -f "$KEYRING_FILE" ]]; then
332
+ echo " ✅ Persistent keyring created at $KEYRING_FILE"
333
+ else
334
+ FOUND_FILE=$(ls "$KEYRING_DIR"/*.keyring 2>/dev/null | head -1 || true)
335
+ if [[ -n "$FOUND_FILE" ]]; then
336
+ echo " ✅ Persistent keyring created at $FOUND_FILE"
337
+ else
338
+ echo " ⚠️ Keyring file not created. Ensure python3 and jeepney are installed."
339
+ echo " Install with: pip3 install jeepney"
340
+ fi
341
+ fi
342
+ fi
343
+ echo ""
344
+
345
+ # ─── 4g. Restore backed-up wallet mnemonics (UPDATE scenario only) ───
346
+ if [[ -n "$BACKUP_FILE" && -f "$BACKUP_FILE" && -s "$BACKUP_FILE" ]]; then
347
+ echo " ── Step 6: Restore wallet mnemonics ──"
348
+ echo ""
349
+
350
+ RESTORE_RESULT=$(NODE_PATH="$NODE_MODULES" node "$BACKUP_SCRIPT" restore "$BACKUP_FILE" 2>&1)
351
+ echo " $RESTORE_RESULT"
352
+
353
+ # Securely delete backup
354
+ shred -u "$BACKUP_FILE" 2>/dev/null || rm -f "$BACKUP_FILE"
355
+ echo " ✅ Temporary backup securely deleted."
356
+ echo ""
357
+ fi
358
+
359
+ # ─── 4h. Docker-specific guidance ───
360
+ if [[ "$IN_CONTAINER" == "true" ]]; then
361
+ echo " ── Docker / Container Notes ──"
362
+ echo ""
363
+ echo " Add to your Dockerfile:"
364
+ echo " RUN apt-get update && apt-get install -y \\"
365
+ echo " gnome-keyring libsecret-tools dbus-user-session \\"
366
+ echo " && rm -rf /var/lib/apt/lists/*"
367
+ echo ""
368
+ echo " Entrypoint wrapper (entrypoint.sh):"
369
+ echo ' #!/bin/bash'
370
+ echo ' export $(dbus-launch)'
371
+ echo ' eval $(gnome-keyring-daemon --start --components=secrets)'
372
+ echo ' # Create login collection if not exists (headless — no GUI prompt)'
373
+ echo ' if [ ! -f ~/.local/share/keyrings/login.keyring ]; then'
374
+ echo ' python3 -c "'
375
+ echo ' from jeepney import new_method_call, DBusAddress'
376
+ echo ' from jeepney.io.blocking import open_dbus_connection'
377
+ echo ' conn = open_dbus_connection(bus=\"SESSION\")'
378
+ echo ' r = conn.send_and_get_reply(new_method_call(DBusAddress(\"/org/freedesktop/secrets\",bus_name=\"org.freedesktop.secrets\",interface=\"org.freedesktop.Secret.Service\"),\"OpenSession\",\"sv\",(\"plain\",(\"s\",\"\"))))'
379
+ echo ' s = r.body[1]'
380
+ echo ' conn.send_and_get_reply(new_method_call(DBusAddress(\"/org/freedesktop/secrets\",bus_name=\"org.gnome.keyring\",interface=\"org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface\"),\"CreateWithMasterPassword\",\"a{sv}(oayays)\",({\"org.freedesktop.Secret.Collection.Label\":(\"s\",\"login\")},(s,b\"\",b\"\",\"text/plain\"))))'
381
+ echo ' conn.close()'
382
+ echo ' "'
383
+ echo ' fi'
384
+ echo ' exec "$@"'
385
+ echo ""
386
+ echo " Persist keyring + wallet data (docker-compose.yml):"
387
+ echo " volumes:"
388
+ echo " - keyring-data:/home/user/.local/share/keyrings"
389
+ echo " - wallet-data:/home/user/.algorand-mcp"
390
+ echo ""
391
+ fi
392
+
393
+ # ─── 4i. Final verification ───
394
+ echo " ── Results ──"
395
+ echo ""
396
+
397
+ # Re-check
398
+ VERIFY_RUNNING=false
399
+ pgrep -u "$USER" gnome-keyring-daemon >/dev/null 2>&1 && VERIFY_RUNNING=true
400
+
401
+ VERIFY_FILES=false
402
+ [[ -d "$KEYRING_DIR" && "$(ls -A "$KEYRING_DIR" 2>/dev/null)" ]] && VERIFY_FILES=true
403
+
404
+ if [[ "$VERIFY_RUNNING" == "true" ]]; then
405
+ echo " ✅ Daemon: Running"
406
+ else
407
+ echo " ✅ Daemon: D-Bus activated (starts on demand)"
408
+ fi
409
+
410
+ if [[ "$VERIFY_FILES" == "true" ]]; then
411
+ echo " ✅ Persistent: Yes (files at $KEYRING_DIR)"
412
+ else
413
+ echo " ⚠️ Persistent: No keyring files yet"
414
+ fi
415
+
416
+ # Verify wallet mnemonics via Node.js
417
+ if [[ -f "$BACKUP_SCRIPT" && "$WALLET_DB_EXISTS" == "true" ]]; then
418
+ VERIFY_RESULT=$(NODE_PATH="$NODE_MODULES" node "$BACKUP_SCRIPT" verify 2>/dev/null | tail -1)
419
+ echo " ✅ Wallets: $VERIFY_RESULT"
420
+ fi
421
+
422
+ echo ""
423
+ echo " Behavior after setup:"
424
+ echo " • Keyring stored on disk, auto-unlocked on boot via D-Bus activation"
425
+ echo " • loginctl linger keeps D-Bus session alive between SSH sessions"
426
+ echo " • Wallet keys persist across reboots — no user interaction needed"
427
+ echo " • Keep agent wallet funds minimal — use QR code top-ups as needed"
428
+ echo ""
package/setup.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  import * as p from "@clack/prompts";
2
- // import { execSync } from "node:child_process";
2
+ import { execSync } from "node:child_process";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
3
5
  import { ALGORAND_MCP, GOPLAUSIBLE_SERVICES } from "./lib/mcp-servers.js";
4
6
 
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+
5
9
  export interface AlgorandPluginConfig {
6
10
  enableX402: boolean;
7
11
  }
@@ -11,34 +15,14 @@ export async function runSetup(
11
15
  ): Promise<AlgorandPluginConfig | null> {
12
16
  p.intro("🔷 Algorand Plugin Setup — powered by GoPlausible");
13
17
 
14
- // // Step 1: Verify algorand-mcp binary is available
15
- // let mcpAvailable = false;
16
- // let mcpPath = "";
17
- // try {
18
- // mcpPath = execSync("npm list @goplausible/algorand-mcp", { encoding: "utf-8" }).trim();
19
- // mcpAvailable = true;
20
- // } catch {
21
- // // Binary not found in PATH
22
- // }
23
-
24
- // if (mcpAvailable) {
25
- // p.note(
26
- // `MCP Server: ${ALGORAND_MCP.name}\n` +
27
- // `Type: ${ALGORAND_MCP.type} (local)\n` +
28
- // `Command: ${ALGORAND_MCP.command}\n` +
29
- // `Path: ${mcpPath}\n` +
30
- // `Status: ✅ Available`,
31
- // "Algorand MCP"
32
- // );
33
- // } else {
34
- // p.log.warn(
35
- // `algorand-mcp binary not found in PATH.\n\n` +
36
- // `Options:\n` +
37
- // ` • Run with npx: npx algorand-mcp\n` +
38
- // ` • Install globally: npm install -g @goplausible/algorand-mcp\n` +
39
- // ` • Add node_modules/.bin to PATH`
40
- // );
41
- // }
18
+ // Step 1: Keyring persistence check & setup
19
+ p.log.step("Checking keyring persistence for wallet storage...");
20
+ try {
21
+ const scriptPath = join(__dirname, "scripts", "setup-keyring.sh");
22
+ execSync(`bash "${scriptPath}" --setup`, { stdio: "inherit" });
23
+ } catch {
24
+ p.log.warn("Keyring setup script failed — you may need to configure manually.");
25
+ }
42
26
 
43
27
  // Step 2: x402 integration
44
28
  const enableX402 = await p.confirm({
@@ -59,7 +43,7 @@ export async function runSetup(
59
43
  p.note(
60
44
  `x402 Micropayments: ${config.enableX402 ? "Enabled" : "Disabled"}\n\n` +
61
45
  `MCP Server Setup:\n` +
62
- ` The Algorand MCP server (${ALGORAND_MCP.command}) provides 99 blockchain tools.\n` +
46
+ ` The Algorand MCP server (${ALGORAND_MCP.command}) provides 107 blockchain tools.\n` +
63
47
  ` x402 micropayment and AP2 mandate verifiable credentials flows are fully supported in Algorand MCP.\n` ,
64
48
  );
65
49
 
@@ -43,8 +43,8 @@ Key points:
43
43
 
44
44
  1. **Check wallet**: `wallet_get_info` with target `network` — verify an account exists and is active
45
45
  2. **If no accounts**: Guide user to create one with `wallet_add_account` (sets nickname and spending limits)
46
- 3. **If needs funding**: Generate ARC-26 QR with `generate_algorand_uri` or direct to testnet faucet: https://lora.algokit.io/testnet/fund
47
- 4. **If needs USDC funding**: Generate ARC-26 QR with `generate_algorand_uri` or direct to testnet faucet: https://faucet.circle.com/
46
+ 3. **If needs funding**: Generate ARC-26 QR with `generate_algorand_qrcode` or direct to testnet faucet: https://lora.algokit.io/testnet/fund
47
+ 4. **If needs USDC funding**: Generate ARC-26 QR with `generate_algorand_qrcode` or direct to testnet faucet: https://faucet.circle.com/
48
48
  5. **Confirm network**: Always confirm which network (`mainnet`, `testnet`, `localnet`) before transactions
49
49
 
50
50
  ## Network Selection
@@ -164,7 +164,7 @@ For atomic (all-or-nothing) multi-transaction groups:
164
164
 
165
165
  **Pera Asset Verification** (3): `api_pera_asset_verification_status`, `api_pera_verified_asset_details`, `api_pera_verified_asset_search` — mainnet asset verification (verified/trusted/suspicious/unverified), detailed asset info with USD value, and search by name/keyword
166
166
 
167
- **ARC-26 URI** (1): `generate_algorand_uri`
167
+ **ARC-26 URI** (1): `generate_algorand_qrcode`
168
168
 
169
169
  **Knowledge Base** (1): `get_knowledge_doc`
170
170
 
@@ -209,6 +209,31 @@ All prices and quantities use **microunits** (1,000,000 = $1.00 or 1 share). Ord
209
209
 
210
210
  > For detailed Alpha Arcade workflows (orderbook mechanics, multi-choice markets, split/merge shares, claiming, collateral model), load the `alpha-arcade-interaction` skill.
211
211
 
212
+ ## QR Code Display (ARC-26 URI)
213
+
214
+ When generating QR codes with `generate_algorand_qrcode`, the tool returns:
215
+ - UTF-8 text QR code (terminal-friendly)
216
+ - PNG image as base64 (web-friendly)
217
+ - URI string
218
+
219
+ **Important:** MCP tool output may not render properly through mcporter → exec pipeline.
220
+ After calling the tool, **extract and paste the QR code directly in your response**:
221
+
222
+ 1. Call the tool and capture output
223
+ 2. Extract the UTF-8 QR block (Unicode block characters)
224
+ 3. Extract the base64 PNG data
225
+ 4. Include both in your reply:
226
+
227
+ ```
228
+ [paste UTF-8 QR here]
229
+ ```
230
+
231
+ ![QR Code](data:image/png;base64,[paste base64 here])
232
+
233
+ URI: `algorand://ADDRESS?amount=X&asset=Y`
234
+
235
+ This ensures the QR renders correctly in both terminal and web interfaces.
236
+
212
237
  ## References
213
238
 
214
239
  For detailed tool documentation:
@@ -782,20 +782,40 @@ Mainnet asset verification via Pera Wallet API. Use to check if assets are legit
782
782
 
783
783
  ## ARC-26 URI Tools
784
784
 
785
- ### generate_algorand_uri
786
- - **Purpose**: Generate an Algorand payment URI and QR code per ARC-26 specification
785
+ ### generate_algorand_qrcode
786
+ - **Purpose**: Generate an Algorand top up payment or asset transferURI and QR code per ARC-26 specification
787
787
  - **Parameters**:
788
788
  ```json
789
789
  {
790
790
  "address": "receiver_address",
791
791
  "label": "Payment label",
792
792
  "amount": 1000000,
793
- "asset": 31566704,
793
+ "asset": 31566704,// optional, if asset transfer; omit or set to 0 for ALGO
794
794
  "note": "Payment note",
795
- "xnote": "Exclusive note"
795
+ "xnote": "Exclusive immutable note"
796
796
  }
797
797
  ```
798
- - **Returns**: URI string + SVG QR code
798
+ - **Returns**: UTF-8 text QR code (terminal-friendly), PNG image as base64 (web-friendly), URI string
799
+
800
+ ### QR Code Display
801
+
802
+ **Important:** MCP tool output may not render properly through mcporter → exec pipeline.
803
+ After calling the tool, **extract and paste the QR code directly in your response**:
804
+
805
+ 1. Call the tool and capture output
806
+ 2. Extract the UTF-8 QR block (Unicode block characters)
807
+ 3. Extract the base64 PNG data
808
+ 4. Include both in your reply:
809
+
810
+ ```
811
+ [paste UTF-8 QR here]
812
+ ```
813
+
814
+ ![QR Code](data:image/png;base64,[paste base64 here])
815
+
816
+ URI: `algorand://ADDRESS?amount=X&asset=Y`
817
+
818
+ This ensures the QR renders correctly in both terminal and web interfaces.
799
819
 
800
820
  ---
801
821
 
@@ -23,7 +23,7 @@ wallet_add_account {
23
23
 
24
24
  ### Step 3: If account needs funding
25
25
  ```
26
- generate_algorand_uri {
26
+ generate_algorand_qrcode {
27
27
  "address": "[wallet_address]",
28
28
  "amount": 5000000,
29
29
  "note": "Fund testnet account"
@@ -33,7 +33,7 @@ Or direct user to: https://lora.algokit.io/testnet/fund
33
33
 
34
34
  ### Step 4: If account needs USDC funding
35
35
  ```
36
- generate_algorand_uri {
36
+ generate_algorand_qrcode {
37
37
  "address": "[wallet_address]",
38
38
  "asset": 10458941, // USDC on testnet
39
39
  "amount": 1000000, // 1 USDC with 6 decimals
@@ -599,7 +599,7 @@ send_raw_transaction {
599
599
  When balance is insufficient, generate an ARC-26 QR code for easy funding:
600
600
 
601
601
  ```
602
- generate_algorand_uri {
602
+ generate_algorand_qrcode {
603
603
  "address": "[wallet_address]",
604
604
  "amount": 5000000,
605
605
  "note": "Fund account for transaction"