@aibtc/mcp-server 1.57.0 → 1.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -15
- package/dist/index.js +150 -47
- package/dist/services/credentials.js +7 -2
- package/dist/services/defi.service.js +23 -11
- package/dist/services/hiro-api.d.ts +10 -0
- package/dist/services/hiro-api.js +30 -8
- package/dist/services/nft.service.js +14 -5
- package/dist/services/pillar-api.service.d.ts +8 -0
- package/dist/services/pillar-api.service.js +13 -1
- package/dist/services/x402.service.js +45 -13
- package/dist/tools/bitcoin.tools.js +14 -0
- package/dist/tools/endpoint.tools.js +3 -1
- package/dist/tools/inbox.tools.js +9 -12
- package/dist/tools/news.tools.js +9 -12
- package/dist/tools/pillar-direct.tools.js +29 -22
- package/dist/tools/sbtc.tools.js +2 -9
- package/dist/tools/scaffold.tools.js +9 -14
- package/dist/tools/settings.tools.js +2 -2
- package/dist/tools/stacking-lottery.tools.js +8 -5
- package/dist/tools/yield-dashboard.tools.js +10 -14
- package/dist/utils/encryption.js +2 -0
- package/package.json +10 -5
- package/skill/SKILL.md +14 -3
- package/skill/references/genesis-lifecycle.md +105 -92
- package/dist/config/bitcoin-constants.d.ts.map +0 -1
- package/dist/config/bitcoin-constants.js.map +0 -1
- package/dist/config/caip.d.ts.map +0 -1
- package/dist/config/caip.js.map +0 -1
- package/dist/config/competition.d.ts.map +0 -1
- package/dist/config/competition.js.map +0 -1
- package/dist/config/contracts.d.ts.map +0 -1
- package/dist/config/contracts.js.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js.map +0 -1
- package/dist/config/networks.d.ts.map +0 -1
- package/dist/config/networks.js.map +0 -1
- package/dist/config/pillar.d.ts.map +0 -1
- package/dist/config/pillar.js.map +0 -1
- package/dist/config/sponsor.d.ts.map +0 -1
- package/dist/config/sponsor.js.map +0 -1
- package/dist/endpoints/index.d.ts.map +0 -1
- package/dist/endpoints/index.js.map +0 -1
- package/dist/endpoints/registry.d.ts.map +0 -1
- package/dist/endpoints/registry.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/services/bitflow.service.d.ts.map +0 -1
- package/dist/services/bitflow.service.js.map +0 -1
- package/dist/services/bns.service.d.ts.map +0 -1
- package/dist/services/bns.service.js.map +0 -1
- package/dist/services/credentials.d.ts.map +0 -1
- package/dist/services/credentials.js.map +0 -1
- package/dist/services/defi.service.d.ts.map +0 -1
- package/dist/services/defi.service.js.map +0 -1
- package/dist/services/erc8004.service.d.ts.map +0 -1
- package/dist/services/erc8004.service.js.map +0 -1
- package/dist/services/hiro-api.d.ts.map +0 -1
- package/dist/services/hiro-api.js.map +0 -1
- package/dist/services/inscription-parser.d.ts.map +0 -1
- package/dist/services/inscription-parser.js.map +0 -1
- package/dist/services/lightning/provider.d.ts.map +0 -1
- package/dist/services/lightning/provider.js.map +0 -1
- package/dist/services/lightning/spark-provider.d.ts.map +0 -1
- package/dist/services/lightning/spark-provider.js.map +0 -1
- package/dist/services/lightning-manager.d.ts.map +0 -1
- package/dist/services/lightning-manager.js.map +0 -1
- package/dist/services/mempool-api.d.ts.map +0 -1
- package/dist/services/mempool-api.js.map +0 -1
- package/dist/services/nft.service.d.ts.map +0 -1
- package/dist/services/nft.service.js.map +0 -1
- package/dist/services/nonce-tracker.d.ts.map +0 -1
- package/dist/services/nonce-tracker.js.map +0 -1
- package/dist/services/pillar-api.service.d.ts.map +0 -1
- package/dist/services/pillar-api.service.js.map +0 -1
- package/dist/services/sbtc-deposit.service.d.ts.map +0 -1
- package/dist/services/sbtc-deposit.service.js.map +0 -1
- package/dist/services/sbtc.service.d.ts.map +0 -1
- package/dist/services/sbtc.service.js.map +0 -1
- package/dist/services/scaffold.service.d.ts.map +0 -1
- package/dist/services/scaffold.service.js.map +0 -1
- package/dist/services/signing-key.service.d.ts.map +0 -1
- package/dist/services/signing-key.service.js.map +0 -1
- package/dist/services/stacking.service.d.ts.map +0 -1
- package/dist/services/stacking.service.js.map +0 -1
- package/dist/services/tenero-api.d.ts.map +0 -1
- package/dist/services/tenero-api.js.map +0 -1
- package/dist/services/tokens.service.d.ts.map +0 -1
- package/dist/services/tokens.service.js.map +0 -1
- package/dist/services/unisat-indexer.d.ts.map +0 -1
- package/dist/services/unisat-indexer.js.map +0 -1
- package/dist/services/wallet-manager.d.ts.map +0 -1
- package/dist/services/wallet-manager.js.map +0 -1
- package/dist/services/x402.service.d.ts.map +0 -1
- package/dist/services/x402.service.js.map +0 -1
- package/dist/tools/arxiv-research.tools.d.ts.map +0 -1
- package/dist/tools/arxiv-research.tools.js.map +0 -1
- package/dist/tools/bitcoin.tools.d.ts.map +0 -1
- package/dist/tools/bitcoin.tools.js.map +0 -1
- package/dist/tools/bitflow.tools.d.ts.map +0 -1
- package/dist/tools/bitflow.tools.js.map +0 -1
- package/dist/tools/bns.tools.d.ts.map +0 -1
- package/dist/tools/bns.tools.js.map +0 -1
- package/dist/tools/bounty-scanner.tools.d.ts.map +0 -1
- package/dist/tools/bounty-scanner.tools.js.map +0 -1
- package/dist/tools/child-inscription.tools.d.ts.map +0 -1
- package/dist/tools/child-inscription.tools.js.map +0 -1
- package/dist/tools/competition.tools.d.ts.map +0 -1
- package/dist/tools/competition.tools.js.map +0 -1
- package/dist/tools/contract.tools.d.ts.map +0 -1
- package/dist/tools/contract.tools.js.map +0 -1
- package/dist/tools/credentials.tools.d.ts.map +0 -1
- package/dist/tools/credentials.tools.js.map +0 -1
- package/dist/tools/defi.tools.d.ts.map +0 -1
- package/dist/tools/defi.tools.js.map +0 -1
- package/dist/tools/dual-stacking.tools.d.ts.map +0 -1
- package/dist/tools/dual-stacking.tools.js.map +0 -1
- package/dist/tools/earning.tools.d.ts.map +0 -1
- package/dist/tools/earning.tools.js.map +0 -1
- package/dist/tools/endpoint.tools.d.ts.map +0 -1
- package/dist/tools/endpoint.tools.js.map +0 -1
- package/dist/tools/erc8004.tools.d.ts.map +0 -1
- package/dist/tools/erc8004.tools.js.map +0 -1
- package/dist/tools/identity.tools.d.ts.map +0 -1
- package/dist/tools/identity.tools.js.map +0 -1
- package/dist/tools/inbox-x402.tools.d.ts.map +0 -1
- package/dist/tools/inbox-x402.tools.js.map +0 -1
- package/dist/tools/inbox.tools.d.ts.map +0 -1
- package/dist/tools/inbox.tools.js.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/jingswap.tools.d.ts.map +0 -1
- package/dist/tools/jingswap.tools.js.map +0 -1
- package/dist/tools/lightning.tools.d.ts.map +0 -1
- package/dist/tools/lightning.tools.js.map +0 -1
- package/dist/tools/mempool.tools.d.ts.map +0 -1
- package/dist/tools/mempool.tools.js.map +0 -1
- package/dist/tools/news.tools.d.ts.map +0 -1
- package/dist/tools/news.tools.js.map +0 -1
- package/dist/tools/nft.tools.d.ts.map +0 -1
- package/dist/tools/nft.tools.js.map +0 -1
- package/dist/tools/nonce.tools.d.ts.map +0 -1
- package/dist/tools/nonce.tools.js.map +0 -1
- package/dist/tools/nostr.tools.d.ts.map +0 -1
- package/dist/tools/nostr.tools.js.map +0 -1
- package/dist/tools/openrouter.tools.d.ts.map +0 -1
- package/dist/tools/openrouter.tools.js.map +0 -1
- package/dist/tools/ordinals-marketplace.tools.d.ts.map +0 -1
- package/dist/tools/ordinals-marketplace.tools.js.map +0 -1
- package/dist/tools/ordinals-p2p.tools.d.ts.map +0 -1
- package/dist/tools/ordinals-p2p.tools.js.map +0 -1
- package/dist/tools/ordinals.tools.d.ts.map +0 -1
- package/dist/tools/ordinals.tools.js.map +0 -1
- package/dist/tools/pillar-direct.tools.d.ts.map +0 -1
- package/dist/tools/pillar-direct.tools.js.map +0 -1
- package/dist/tools/pillar.tools.d.ts.map +0 -1
- package/dist/tools/pillar.tools.js.map +0 -1
- package/dist/tools/psbt.helpers.d.ts.map +0 -1
- package/dist/tools/psbt.helpers.js.map +0 -1
- package/dist/tools/psbt.tools.d.ts.map +0 -1
- package/dist/tools/psbt.tools.js.map +0 -1
- package/dist/tools/query.tools.d.ts.map +0 -1
- package/dist/tools/query.tools.js.map +0 -1
- package/dist/tools/relay-diagnostic.tools.d.ts.map +0 -1
- package/dist/tools/relay-diagnostic.tools.js.map +0 -1
- package/dist/tools/reputation.tools.d.ts.map +0 -1
- package/dist/tools/reputation.tools.js.map +0 -1
- package/dist/tools/runes.tools.d.ts.map +0 -1
- package/dist/tools/runes.tools.js.map +0 -1
- package/dist/tools/sbtc.tools.d.ts.map +0 -1
- package/dist/tools/sbtc.tools.js.map +0 -1
- package/dist/tools/scaffold.tools.d.ts.map +0 -1
- package/dist/tools/scaffold.tools.js.map +0 -1
- package/dist/tools/schemas.d.ts.map +0 -1
- package/dist/tools/schemas.js.map +0 -1
- package/dist/tools/settings.tools.d.ts.map +0 -1
- package/dist/tools/settings.tools.js.map +0 -1
- package/dist/tools/signing.tools.d.ts.map +0 -1
- package/dist/tools/signing.tools.js.map +0 -1
- package/dist/tools/skill-mappings.d.ts.map +0 -1
- package/dist/tools/skill-mappings.js.map +0 -1
- package/dist/tools/souldinals.tools.d.ts.map +0 -1
- package/dist/tools/souldinals.tools.js.map +0 -1
- package/dist/tools/stacking-lottery.tools.d.ts.map +0 -1
- package/dist/tools/stacking-lottery.tools.js.map +0 -1
- package/dist/tools/stacking.tools.d.ts.map +0 -1
- package/dist/tools/stacking.tools.js.map +0 -1
- package/dist/tools/stacks-market.tools.d.ts.map +0 -1
- package/dist/tools/stacks-market.tools.js.map +0 -1
- package/dist/tools/styx.tools.d.ts.map +0 -1
- package/dist/tools/styx.tools.js.map +0 -1
- package/dist/tools/taproot-multisig.tools.d.ts.map +0 -1
- package/dist/tools/taproot-multisig.tools.js.map +0 -1
- package/dist/tools/tenero.tools.d.ts.map +0 -1
- package/dist/tools/tenero.tools.js.map +0 -1
- package/dist/tools/tokens.tools.d.ts.map +0 -1
- package/dist/tools/tokens.tools.js.map +0 -1
- package/dist/tools/transfer.tools.d.ts.map +0 -1
- package/dist/tools/transfer.tools.js.map +0 -1
- package/dist/tools/wallet-management.tools.d.ts.map +0 -1
- package/dist/tools/wallet-management.tools.js.map +0 -1
- package/dist/tools/wallet.tools.d.ts.map +0 -1
- package/dist/tools/wallet.tools.js.map +0 -1
- package/dist/tools/yield-dashboard.tools.d.ts.map +0 -1
- package/dist/tools/yield-dashboard.tools.js.map +0 -1
- package/dist/tools/yield-hunter.tools.d.ts.map +0 -1
- package/dist/tools/yield-hunter.tools.js.map +0 -1
- package/dist/transactions/bitcoin-builder.d.ts.map +0 -1
- package/dist/transactions/bitcoin-builder.js.map +0 -1
- package/dist/transactions/builder.d.ts.map +0 -1
- package/dist/transactions/builder.js.map +0 -1
- package/dist/transactions/child-inscription-builder.d.ts.map +0 -1
- package/dist/transactions/child-inscription-builder.js.map +0 -1
- package/dist/transactions/clarity-values.d.ts.map +0 -1
- package/dist/transactions/clarity-values.js.map +0 -1
- package/dist/transactions/inscription-builder.d.ts.map +0 -1
- package/dist/transactions/inscription-builder.js.map +0 -1
- package/dist/transactions/post-conditions.d.ts.map +0 -1
- package/dist/transactions/post-conditions.js.map +0 -1
- package/dist/transactions/rune-transfer-builder.d.ts.map +0 -1
- package/dist/transactions/rune-transfer-builder.js.map +0 -1
- package/dist/transactions/runestone-builder.d.ts.map +0 -1
- package/dist/transactions/runestone-builder.js.map +0 -1
- package/dist/transactions/sponsor-builder.d.ts.map +0 -1
- package/dist/transactions/sponsor-builder.js.map +0 -1
- package/dist/utils/bip322.d.ts.map +0 -1
- package/dist/utils/bip322.js.map +0 -1
- package/dist/utils/bitcoin.d.ts.map +0 -1
- package/dist/utils/bitcoin.js.map +0 -1
- package/dist/utils/encryption.d.ts.map +0 -1
- package/dist/utils/encryption.js.map +0 -1
- package/dist/utils/erc8004-helpers.d.ts.map +0 -1
- package/dist/utils/erc8004-helpers.js.map +0 -1
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/fee.d.ts.map +0 -1
- package/dist/utils/fee.js.map +0 -1
- package/dist/utils/formatting.d.ts.map +0 -1
- package/dist/utils/formatting.js.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/l402-protocol.d.ts.map +0 -1
- package/dist/utils/l402-protocol.js.map +0 -1
- package/dist/utils/redact.d.ts.map +0 -1
- package/dist/utils/redact.js.map +0 -1
- package/dist/utils/relay-health.d.ts.map +0 -1
- package/dist/utils/relay-health.js.map +0 -1
- package/dist/utils/storage.d.ts.map +0 -1
- package/dist/utils/storage.js.map +0 -1
- package/dist/utils/tokens.d.ts.map +0 -1
- package/dist/utils/tokens.js.map +0 -1
- package/dist/utils/x402-payment-logging.d.ts.map +0 -1
- package/dist/utils/x402-payment-logging.js.map +0 -1
- package/dist/utils/x402-payment-state.d.ts.map +0 -1
- package/dist/utils/x402-payment-state.js.map +0 -1
- package/dist/utils/x402-protocol.d.ts.map +0 -1
- package/dist/utils/x402-protocol.js.map +0 -1
- package/dist/utils/x402-recovery.d.ts.map +0 -1
- package/dist/utils/x402-recovery.js.map +0 -1
- package/dist/yield-hunter/index.d.ts.map +0 -1
- package/dist/yield-hunter/index.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
|
-
import { makeSTXTokenTransfer, makeContractCall, uintCV, principalCV, noneCV, PostConditionMode, } from "@stacks/transactions";
|
|
2
|
+
import { makeSTXTokenTransfer, makeContractCall, uintCV, principalCV, noneCV, Pc, PostConditionMode, } from "@stacks/transactions";
|
|
3
3
|
import { decodePaymentRequired, encodePaymentPayload, generatePaymentId, buildPaymentIdentifierExtension, X402_HEADERS, } from "../utils/x402-protocol.js";
|
|
4
4
|
import { generateWallet, getStxAddress } from "@stacks/wallet-sdk";
|
|
5
5
|
import { NETWORK, API_URL, getStacksNetwork } from "../config/networks.js";
|
|
@@ -11,6 +11,7 @@ import { getHiroApi } from "./hiro-api.js";
|
|
|
11
11
|
import { createHash } from "crypto";
|
|
12
12
|
import { InsufficientBalanceError } from "../utils/errors.js";
|
|
13
13
|
import { getContracts, parseContractId } from "../config/contracts.js";
|
|
14
|
+
import { resolveDefaultFee } from "../utils/fee.js";
|
|
14
15
|
import { classifyCanonicalPaymentStatus, formatCanonicalPaymentStatus, resolveCanonicalPaymentStatus, } from "../utils/x402-payment-state.js";
|
|
15
16
|
import { extractPaymentIdFromPaymentSignature } from "../utils/x402-recovery.js";
|
|
16
17
|
import { emitCanonicalPaymentDecisionLogs, emitCanonicalPaymentPollLogs, emitPaymentLog, } from "../utils/x402-payment-logging.js";
|
|
@@ -42,6 +43,16 @@ function parseSatsCap(envName, fallback) {
|
|
|
42
43
|
return parsed;
|
|
43
44
|
}
|
|
44
45
|
const L402_MAX_SATS_PER_INVOICE = parseSatsCap("L402_MAX_SATS_PER_INVOICE", DEFAULT_L402_MAX_SATS);
|
|
46
|
+
/**
|
|
47
|
+
* Per-payment caps for the Stacks x402 interceptor, mirroring the L402 cap
|
|
48
|
+
* above: a malicious endpoint can demand an arbitrary amount in its 402
|
|
49
|
+
* response and an autoApprove'd call would pay it, bounded only by wallet
|
|
50
|
+
* balance. Known registry endpoints cost at most 0.02 STX / 100 sats, so the
|
|
51
|
+
* defaults leave generous headroom. Overridable via env vars.
|
|
52
|
+
*/
|
|
53
|
+
const X402_MAX_USTX_PER_PAYMENT = parseSatsCap("X402_MAX_USTX_PER_PAYMENT", 1_000_000 // 1 STX
|
|
54
|
+
);
|
|
55
|
+
const X402_MAX_SATS_PER_PAYMENT = parseSatsCap("X402_MAX_SATS_PER_PAYMENT", 10_000);
|
|
45
56
|
// Track payment attempts per client instance (auto-cleanup via WeakMap)
|
|
46
57
|
const paymentAttempts = new WeakMap();
|
|
47
58
|
// Track 429 retry counts per client instance
|
|
@@ -457,6 +468,16 @@ export async function createApiClient(baseUrl, options) {
|
|
|
457
468
|
const networks = paymentRequired.accepts.map((a) => a.network).join(", ");
|
|
458
469
|
return Promise.reject(new Error(`No compatible Stacks payment option found. Available networks: ${networks}`));
|
|
459
470
|
}
|
|
471
|
+
const tokenType = detectTokenType(selectedOption.asset);
|
|
472
|
+
const amount = BigInt(selectedOption.amount);
|
|
473
|
+
const isSbtc = tokenType === "sBTC";
|
|
474
|
+
const cap = isSbtc ? X402_MAX_SATS_PER_PAYMENT : X402_MAX_USTX_PER_PAYMENT;
|
|
475
|
+
const unit = isSbtc ? "sats" : "uSTX";
|
|
476
|
+
if (amount > BigInt(Math.floor(cap))) {
|
|
477
|
+
return Promise.reject(new Error(`x402 payment of ${amount} ${unit} exceeds the per-payment cap of ${cap} ${unit}. ` +
|
|
478
|
+
`If this cost is expected, raise the cap via the ` +
|
|
479
|
+
`${isSbtc ? "X402_MAX_SATS_PER_PAYMENT" : "X402_MAX_USTX_PER_PAYMENT"} env var.`));
|
|
480
|
+
}
|
|
460
481
|
// Lazy-load account on first 402 — free endpoints never reach here
|
|
461
482
|
const acct = await ensureAccount();
|
|
462
483
|
// Verify the payment network matches our configured network
|
|
@@ -476,9 +497,11 @@ export async function createApiClient(baseUrl, options) {
|
|
|
476
497
|
account: acct,
|
|
477
498
|
});
|
|
478
499
|
}
|
|
479
|
-
// Build a sponsored signed transaction
|
|
480
|
-
|
|
481
|
-
|
|
500
|
+
// Build a non-sponsored signed transaction: the sender pays its own
|
|
501
|
+
// STX gas (fee + nonce auto-estimated by @stacks), and the x402
|
|
502
|
+
// facilitator simply verifies and broadcasts it. No relay sponsorship
|
|
503
|
+
// — this is the same tx shape the x402-stacks lib produces, and the
|
|
504
|
+
// balance check in onBeforePayment already accounts for the gas.
|
|
482
505
|
const networkName = getStacksNetwork(acct.network);
|
|
483
506
|
let transaction;
|
|
484
507
|
if (tokenType === "sBTC") {
|
|
@@ -496,9 +519,18 @@ export async function createApiClient(baseUrl, options) {
|
|
|
496
519
|
],
|
|
497
520
|
senderKey: acct.privateKey,
|
|
498
521
|
network: networkName,
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
522
|
+
// The payment amount is known at build time, so pin it: a
|
|
523
|
+
// compromised token contract must not be able to pull more.
|
|
524
|
+
postConditionMode: PostConditionMode.Deny,
|
|
525
|
+
postConditions: [
|
|
526
|
+
Pc.principal(acct.address)
|
|
527
|
+
.willSendEq(amount)
|
|
528
|
+
.ft(contracts.SBTC_TOKEN, "sbtc-token"),
|
|
529
|
+
],
|
|
530
|
+
// Clamped medium fee — do NOT let @stacks auto-estimate, since
|
|
531
|
+
// Hiro's contract_call high_priority tier is polluted by outliers
|
|
532
|
+
// (observed >2000 STX). resolveDefaultFee caps it at 0.05 STX.
|
|
533
|
+
fee: await resolveDefaultFee(acct.network, "contract_call"),
|
|
502
534
|
});
|
|
503
535
|
}
|
|
504
536
|
else {
|
|
@@ -508,8 +540,7 @@ export async function createApiClient(baseUrl, options) {
|
|
|
508
540
|
senderKey: acct.privateKey,
|
|
509
541
|
network: networkName,
|
|
510
542
|
memo: "",
|
|
511
|
-
|
|
512
|
-
fee: 0n,
|
|
543
|
+
fee: await resolveDefaultFee(acct.network, "token_transfer"),
|
|
513
544
|
});
|
|
514
545
|
}
|
|
515
546
|
const txHex = "0x" + transaction.serialize();
|
|
@@ -716,8 +747,9 @@ export async function checkSufficientBalance(account, amount, asset, sponsored =
|
|
|
716
747
|
const hiroApiForSbtc = getHiroApi(account.network);
|
|
717
748
|
const stxInfoForSbtc = await hiroApiForSbtc.getStxBalance(account.address);
|
|
718
749
|
const stxBalanceForSbtc = BigInt(stxInfoForSbtc.balance);
|
|
719
|
-
|
|
720
|
-
|
|
750
|
+
// Same clamped fee the interceptor actually sets on the sBTC contract
|
|
751
|
+
// call — so this check is exact, not a high-priority over-estimate.
|
|
752
|
+
const estimatedSbtcFee = await resolveDefaultFee(account.network, "contract_call");
|
|
721
753
|
if (stxBalanceForSbtc < estimatedSbtcFee) {
|
|
722
754
|
const stxShortfall = estimatedSbtcFee - stxBalanceForSbtc;
|
|
723
755
|
throw new InsufficientBalanceError(`Insufficient STX balance to cover sBTC transfer fee: need ${formatStx(estimatedSbtcFee.toString())} estimated fee, ` +
|
|
@@ -733,8 +765,8 @@ export async function checkSufficientBalance(account, amount, asset, sponsored =
|
|
|
733
765
|
const balance = BigInt(balanceInfo.balance);
|
|
734
766
|
let totalRequired = requiredAmount;
|
|
735
767
|
if (!sponsored) {
|
|
736
|
-
|
|
737
|
-
const estimatedFee =
|
|
768
|
+
// Same clamped fee the interceptor sets on the STX transfer.
|
|
769
|
+
const estimatedFee = await resolveDefaultFee(account.network, "token_transfer");
|
|
738
770
|
totalRequired = requiredAmount + estimatedFee;
|
|
739
771
|
}
|
|
740
772
|
if (balance >= totalRequired)
|
|
@@ -33,6 +33,19 @@ async function getBtcAddress(providedAddress) {
|
|
|
33
33
|
throw new Error("No Bitcoin address provided and wallet is not unlocked. " +
|
|
34
34
|
"Either provide an address or unlock your wallet first.");
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Reject recipient addresses that belong to the other Bitcoin network before
|
|
38
|
+
* any UTXO fetching. The tx builder (@scure/btc-signer) rejects these too, but
|
|
39
|
+
* late and with a cryptic bech32/version-byte error.
|
|
40
|
+
*/
|
|
41
|
+
function validateRecipientNetwork(address) {
|
|
42
|
+
const prefixes = NETWORK === "mainnet" ? ["bc1", "1", "3"] : ["tb1", "m", "n", "2"];
|
|
43
|
+
if (!prefixes.some((p) => address.startsWith(p))) {
|
|
44
|
+
throw new Error(`Recipient "${address}" is not a ${NETWORK} address. ` +
|
|
45
|
+
`Expected a prefix of ${prefixes.join(", ")} — check that the address ` +
|
|
46
|
+
`matches the configured NETWORK.`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
36
49
|
/**
|
|
37
50
|
* Format satoshis as BTC string
|
|
38
51
|
*/
|
|
@@ -231,6 +244,7 @@ export function registerBitcoinTools(server) {
|
|
|
231
244
|
},
|
|
232
245
|
}, async ({ recipient, amount, feeRate, includeOrdinals }) => {
|
|
233
246
|
try {
|
|
247
|
+
validateRecipientNetwork(recipient);
|
|
234
248
|
// Get wallet account (requires unlocked wallet)
|
|
235
249
|
const walletManager = getWalletManager();
|
|
236
250
|
const account = walletManager.getActiveAccount();
|
|
@@ -276,7 +276,9 @@ For aibtc.com inbox messages, use send_inbox_message_direct instead — it signs
|
|
|
276
276
|
const api = await createApiClient(parsed.baseUrl, {
|
|
277
277
|
toolName: "execute_x402_endpoint",
|
|
278
278
|
onBeforePayment: async (requirements) => {
|
|
279
|
-
|
|
279
|
+
// Non-sponsored: sender pays its own gas, so validate STX for the
|
|
280
|
+
// fee too (sponsored=false is the default, passed explicitly here).
|
|
281
|
+
await checkSufficientBalance(requirements.account, requirements.amount, requirements.asset, false);
|
|
280
282
|
},
|
|
281
283
|
});
|
|
282
284
|
const response = await api.request({ method, url: parsed.requestPath, params, data });
|
|
@@ -40,21 +40,18 @@ async function getNextNonce(address) {
|
|
|
40
40
|
const hiroApi = getHiroApi(NETWORK);
|
|
41
41
|
const accountInfo = await hiroApi.getAccountInfo(address);
|
|
42
42
|
const confirmedNonce = accountInfo.nonce;
|
|
43
|
+
// The mempool scan is what makes the nonce safe against pending txs —
|
|
44
|
+
// silently skipping it on failure risks a nonce collision, so let it fail.
|
|
43
45
|
let highestMempoolNonce = -1;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
highestMempoolNonce = tx.nonce;
|
|
52
|
-
}
|
|
46
|
+
const mempool = await hiroApi.getMempoolTransactions({
|
|
47
|
+
sender_address: address,
|
|
48
|
+
limit: 50,
|
|
49
|
+
});
|
|
50
|
+
for (const tx of mempool.results) {
|
|
51
|
+
if (tx.nonce > highestMempoolNonce) {
|
|
52
|
+
highestMempoolNonce = tx.nonce;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
catch {
|
|
56
|
-
// Non-fatal: fall back to confirmed nonce only
|
|
57
|
-
}
|
|
58
55
|
const chainNext = Math.max(confirmedNonce, highestMempoolNonce + 1);
|
|
59
56
|
// 3. Reconcile tracker with chain state
|
|
60
57
|
await reconcileWithChain(address, chainNext);
|
package/dist/tools/news.tools.js
CHANGED
|
@@ -56,21 +56,18 @@ async function getNextNonce(address) {
|
|
|
56
56
|
const hiroApi = getHiroApi(NETWORK);
|
|
57
57
|
const accountInfo = await hiroApi.getAccountInfo(address);
|
|
58
58
|
const confirmedNonce = accountInfo.nonce;
|
|
59
|
+
// The mempool scan is what makes the nonce safe against pending txs —
|
|
60
|
+
// silently skipping it on failure risks a nonce collision, so let it fail.
|
|
59
61
|
let highestMempoolNonce = -1;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
highestMempoolNonce = tx.nonce;
|
|
68
|
-
}
|
|
62
|
+
const mempool = await hiroApi.getMempoolTransactions({
|
|
63
|
+
sender_address: address,
|
|
64
|
+
limit: 50,
|
|
65
|
+
});
|
|
66
|
+
for (const tx of mempool.results) {
|
|
67
|
+
if (tx.nonce > highestMempoolNonce) {
|
|
68
|
+
highestMempoolNonce = tx.nonce;
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
-
catch {
|
|
72
|
-
// Non-fatal
|
|
73
|
-
}
|
|
74
71
|
const chainNext = Math.max(confirmedNonce, highestMempoolNonce + 1);
|
|
75
72
|
await reconcileWithChain(address, chainNext);
|
|
76
73
|
return Math.max(chainNext, localNext ?? 0);
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import crypto from "crypto";
|
|
3
3
|
import { tupleCV, stringAsciiCV, uintCV, principalCV, noneCV, trueCV, falseCV, } from "@stacks/transactions";
|
|
4
4
|
import { getSigningKeyService, generateAuthId, } from "../services/signing-key.service.js";
|
|
5
|
-
import { getPillarApi } from "../services/pillar-api.service.js";
|
|
5
|
+
import { getPillarApi, PillarApiError } from "../services/pillar-api.service.js";
|
|
6
6
|
import { getHiroApi } from "../services/hiro-api.js";
|
|
7
7
|
import { NETWORK } from "../config/networks.js";
|
|
8
8
|
import { PILLAR_API_KEY } from "../config/pillar.js";
|
|
@@ -497,8 +497,12 @@ export function registerPillarDirectTools(server) {
|
|
|
497
497
|
const walletInfo = await api.get(`/api/smart-wallet/${walletName}`);
|
|
498
498
|
walletStatus = walletInfo.data?.status || null;
|
|
499
499
|
}
|
|
500
|
-
catch {
|
|
501
|
-
//
|
|
500
|
+
catch (error) {
|
|
501
|
+
// 404 = wallet not registered in backend. Anything else (network,
|
|
502
|
+
// 5xx) must surface, or an initialized wallet gets misreported.
|
|
503
|
+
if (!(error instanceof PillarApiError && error.status === 404)) {
|
|
504
|
+
throw error;
|
|
505
|
+
}
|
|
502
506
|
}
|
|
503
507
|
if (!walletStatus || walletStatus === "pending_init") {
|
|
504
508
|
return createJsonResponse({
|
|
@@ -554,8 +558,11 @@ export function registerPillarDirectTools(server) {
|
|
|
554
558
|
});
|
|
555
559
|
position = unwindQuote.data;
|
|
556
560
|
}
|
|
557
|
-
catch {
|
|
558
|
-
//
|
|
561
|
+
catch (error) {
|
|
562
|
+
// 4xx = fresh wallet with no position. Infra failures must surface.
|
|
563
|
+
if (!(error instanceof PillarApiError && error.status < 500)) {
|
|
564
|
+
throw error;
|
|
565
|
+
}
|
|
559
566
|
}
|
|
560
567
|
return createJsonResponse({
|
|
561
568
|
success: true,
|
|
@@ -1146,21 +1153,16 @@ export function registerPillarDirectTools(server) {
|
|
|
1146
1153
|
const frac = (micro % BigInt(1_000_000)).toString().padStart(6, "0");
|
|
1147
1154
|
return `${whole}.${frac} STX`;
|
|
1148
1155
|
};
|
|
1149
|
-
// Fetch PoX cycle info
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
};
|
|
1160
|
-
}
|
|
1161
|
-
catch {
|
|
1162
|
-
// PoX info fetch failed, continue without it
|
|
1163
|
-
}
|
|
1156
|
+
// Fetch PoX cycle info — a global endpoint with no "not found" case,
|
|
1157
|
+
// so any failure is infrastructure and should surface.
|
|
1158
|
+
const pox = await hiro.getPoxInfo();
|
|
1159
|
+
const poxInfo = {
|
|
1160
|
+
currentCycleId: pox.current_cycle.id,
|
|
1161
|
+
nextCycleId: pox.next_cycle.id,
|
|
1162
|
+
blocksUntilNextCycle: pox.next_cycle.blocks_until_reward_phase,
|
|
1163
|
+
minAmountUstx: pox.min_amount_ustx,
|
|
1164
|
+
isPoxActive: pox.current_cycle.is_pox_active,
|
|
1165
|
+
};
|
|
1164
1166
|
// Check enrollment status via backend
|
|
1165
1167
|
const api = getPillarApi();
|
|
1166
1168
|
const contractParts = session.smartWallet.split(".");
|
|
@@ -1175,8 +1177,13 @@ export function registerPillarDirectTools(server) {
|
|
|
1175
1177
|
};
|
|
1176
1178
|
}
|
|
1177
1179
|
}
|
|
1178
|
-
catch {
|
|
1179
|
-
//
|
|
1180
|
+
catch (error) {
|
|
1181
|
+
// 404 = wallet not registered, so not enrolled. Other failures
|
|
1182
|
+
// must surface — defaulting to enrolled:false on a backend outage
|
|
1183
|
+
// misreports an enrolled wallet and could prompt re-enrollment.
|
|
1184
|
+
if (!(error instanceof PillarApiError && error.status === 404)) {
|
|
1185
|
+
throw error;
|
|
1186
|
+
}
|
|
1180
1187
|
}
|
|
1181
1188
|
const isStacking = lockedMicro > BigInt(0);
|
|
1182
1189
|
return createJsonResponse({
|
package/dist/tools/sbtc.tools.js
CHANGED
|
@@ -283,16 +283,9 @@ Signers later process the request and send BTC on L1.`,
|
|
|
283
283
|
description: "Get information about how to deposit BTC to receive sBTC.",
|
|
284
284
|
}, async () => {
|
|
285
285
|
try {
|
|
286
|
-
//
|
|
286
|
+
// getActiveAccount returns null when locked - generic instructions then
|
|
287
287
|
const walletManager = getWalletManager();
|
|
288
|
-
|
|
289
|
-
try {
|
|
290
|
-
account = walletManager.getActiveAccount();
|
|
291
|
-
}
|
|
292
|
-
catch {
|
|
293
|
-
// Wallet not unlocked - return generic instructions
|
|
294
|
-
account = null;
|
|
295
|
-
}
|
|
288
|
+
const account = walletManager.getActiveAccount();
|
|
296
289
|
// If wallet is unlocked and has Taproot keys, generate real deposit address
|
|
297
290
|
if (account?.taprootPublicKey) {
|
|
298
291
|
const depositService = getSbtcDepositService(NETWORK);
|
|
@@ -8,20 +8,15 @@ import { getWalletAddress } from "../services/x402.service.js";
|
|
|
8
8
|
* Check if a directory contains an existing x402 project
|
|
9
9
|
*/
|
|
10
10
|
async function isExistingX402Project(dir) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return hasIndex && hasMiddleware && hasPackage;
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
11
|
+
const indexPath = path.join(dir, "src", "index.ts");
|
|
12
|
+
const middlewarePath = path.join(dir, "src", "x402-middleware.ts");
|
|
13
|
+
const packagePath = path.join(dir, "package.json");
|
|
14
|
+
const [hasIndex, hasMiddleware, hasPackage] = await Promise.all([
|
|
15
|
+
fs.access(indexPath).then(() => true).catch(() => false),
|
|
16
|
+
fs.access(middlewarePath).then(() => true).catch(() => false),
|
|
17
|
+
fs.access(packagePath).then(() => true).catch(() => false),
|
|
18
|
+
]);
|
|
19
|
+
return hasIndex && hasMiddleware && hasPackage;
|
|
25
20
|
}
|
|
26
21
|
/**
|
|
27
22
|
* Validate that a project directory does not already exist.
|
|
@@ -198,7 +198,7 @@ Example: http://localhost:3999`,
|
|
|
198
198
|
server.registerTool("get_server_version", {
|
|
199
199
|
description: `Check the currently running MCP server version and compare with the latest published version on npm.
|
|
200
200
|
Use this to detect if you're running a stale cached version (common with npx).
|
|
201
|
-
If your version is outdated, clear the npx cache and
|
|
201
|
+
If your version is outdated, clear the npx cache and restart your MCP client: npx clear-npx-cache (the client config already points at @latest, so the next launch pulls the newest version).`,
|
|
202
202
|
inputSchema: {},
|
|
203
203
|
}, async () => {
|
|
204
204
|
try {
|
|
@@ -237,7 +237,7 @@ If your version is outdated, clear the npx cache and reinstall: npx clear-npx-ca
|
|
|
237
237
|
updateAvailable,
|
|
238
238
|
package: "@aibtc/mcp-server",
|
|
239
239
|
hint: updateAvailable
|
|
240
|
-
? "⚠️ Update available! Clear npx cache and
|
|
240
|
+
? "⚠️ Update available! Clear the npx cache and restart your MCP client: npx clear-npx-cache (config already points at @latest, so the next launch pulls the newest version)."
|
|
241
241
|
: fetched
|
|
242
242
|
? "✅ Running the latest version"
|
|
243
243
|
: "Unable to verify latest version (network error)",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { uintCV, contractPrincipalCV, PostConditionMode, deserializeCV, cvToJSON, } from "@stacks/transactions";
|
|
3
3
|
import { getAccount, NETWORK } from "../services/x402.service.js";
|
|
4
|
-
import { getHiroApi } from "../services/hiro-api.js";
|
|
4
|
+
import { getHiroApi, isNotFoundError } from "../services/hiro-api.js";
|
|
5
5
|
import { callContract } from "../transactions/builder.js";
|
|
6
6
|
import { getExplorerTxUrl } from "../config/networks.js";
|
|
7
7
|
import { createJsonResponse, createErrorResponse } from "../utils/index.js";
|
|
@@ -100,14 +100,17 @@ Note: Stackspot is only available on mainnet.`,
|
|
|
100
100
|
try {
|
|
101
101
|
currentValueUstx = await callPotReadOnly(pot.contractName, "get-pot-value", []);
|
|
102
102
|
}
|
|
103
|
-
catch {
|
|
104
|
-
// pot
|
|
103
|
+
catch (error) {
|
|
104
|
+
// 404 = pot contract not deployed; other failures surface
|
|
105
|
+
if (!isNotFoundError(error))
|
|
106
|
+
throw error;
|
|
105
107
|
}
|
|
106
108
|
try {
|
|
107
109
|
isLocked = await callPotReadOnly(pot.contractName, "is-locked", []);
|
|
108
110
|
}
|
|
109
|
-
catch {
|
|
110
|
-
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (!isNotFoundError(error))
|
|
113
|
+
throw error;
|
|
111
114
|
}
|
|
112
115
|
return {
|
|
113
116
|
name: pot.name,
|
|
@@ -251,21 +251,17 @@ async function readStackingPosition(walletAddress) {
|
|
|
251
251
|
return pos;
|
|
252
252
|
}
|
|
253
253
|
async function getWalletBalances(walletAddress) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
return { stxMicroStx: 0, sbtcSats: 0 };
|
|
258
|
-
const data = (await res.json());
|
|
259
|
-
const stxMicroStx = parseInt(data.stx?.balance ?? "0", 10);
|
|
260
|
-
const sbtcKey = Object.keys(data.fungible_tokens ?? {}).find((k) => k.toLowerCase().includes("sbtc"));
|
|
261
|
-
const sbtcSats = sbtcKey
|
|
262
|
-
? parseInt(data.fungible_tokens?.[sbtcKey]?.balance ?? "0", 10)
|
|
263
|
-
: 0;
|
|
264
|
-
return { stxMicroStx, sbtcSats };
|
|
265
|
-
}
|
|
266
|
-
catch {
|
|
267
|
-
return { stxMicroStx: 0, sbtcSats: 0 };
|
|
254
|
+
const res = await fetch(`${MAINNET_HIRO_API}/extended/v1/address/${walletAddress}/balances`);
|
|
255
|
+
if (!res.ok) {
|
|
256
|
+
throw new Error(`Failed to fetch wallet balances for ${walletAddress}: HTTP ${res.status}`);
|
|
268
257
|
}
|
|
258
|
+
const data = (await res.json());
|
|
259
|
+
const stxMicroStx = parseInt(data.stx?.balance ?? "0", 10);
|
|
260
|
+
const sbtcKey = Object.keys(data.fungible_tokens ?? {}).find((k) => k.toLowerCase().includes("sbtc"));
|
|
261
|
+
const sbtcSats = sbtcKey
|
|
262
|
+
? parseInt(data.fungible_tokens?.[sbtcKey]?.balance ?? "0", 10)
|
|
263
|
+
: 0;
|
|
264
|
+
return { stxMicroStx, sbtcSats };
|
|
269
265
|
}
|
|
270
266
|
async function checkZestV2() {
|
|
271
267
|
try {
|
package/dist/utils/encryption.js
CHANGED
|
@@ -37,6 +37,7 @@ export async function encrypt(plaintext, password) {
|
|
|
37
37
|
const key = await deriveKey(password, salt);
|
|
38
38
|
// Encrypt with AES-256-GCM
|
|
39
39
|
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
40
|
+
key.fill(0); // cipher holds its own copy
|
|
40
41
|
const encrypted = Buffer.concat([
|
|
41
42
|
cipher.update(plaintext, "utf8"),
|
|
42
43
|
cipher.final(),
|
|
@@ -64,6 +65,7 @@ export async function decrypt(encrypted, password) {
|
|
|
64
65
|
const key = await deriveKeyWithParams(password, salt, encrypted.scryptParams);
|
|
65
66
|
// Decrypt with AES-256-GCM
|
|
66
67
|
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
|
|
68
|
+
key.fill(0); // decipher holds its own copy
|
|
67
69
|
decipher.setAuthTag(authTag);
|
|
68
70
|
try {
|
|
69
71
|
const decrypted = Buffer.concat([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aibtc/mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.59.0",
|
|
4
4
|
"description": "Bitcoin-native MCP server for AI agents: BTC/STX wallets, DeFi yield, sBTC peg, NFTs, and x402 payments.",
|
|
5
5
|
"mcpName": "io.github.aibtcdev/mcp-server",
|
|
6
6
|
"type": "module",
|
|
@@ -18,9 +18,13 @@
|
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|
|
21
|
+
"!dist/**/*.map",
|
|
21
22
|
"README.md",
|
|
22
23
|
"skill"
|
|
23
24
|
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18.0.0"
|
|
27
|
+
},
|
|
24
28
|
"keywords": [
|
|
25
29
|
"claude",
|
|
26
30
|
"claude-code",
|
|
@@ -60,7 +64,7 @@
|
|
|
60
64
|
"@stacks/transactions": "^7.3.1",
|
|
61
65
|
"@stacks/wallet-sdk": "^7.2.0",
|
|
62
66
|
"alex-sdk": "^3.2.1",
|
|
63
|
-
"axios": "^1.
|
|
67
|
+
"axios": "^1.17.0",
|
|
64
68
|
"dotenv": "^17.3.1",
|
|
65
69
|
"light-bolt11-decoder": "^3.2.0",
|
|
66
70
|
"micro-ordinals": "^0.3.0",
|
|
@@ -68,7 +72,7 @@
|
|
|
68
72
|
"path-to-regexp": "^8.4.0",
|
|
69
73
|
"sbtc": "^0.3.2",
|
|
70
74
|
"tslib": "^2.8.1",
|
|
71
|
-
"ws": "^8.
|
|
75
|
+
"ws": "^8.21.0",
|
|
72
76
|
"x402-stacks": "^2.0.3",
|
|
73
77
|
"zod": "^4.3.6"
|
|
74
78
|
},
|
|
@@ -77,9 +81,10 @@
|
|
|
77
81
|
"@types/ws": "^8.18.1",
|
|
78
82
|
"tsx": "^4.21.0",
|
|
79
83
|
"typescript": "^5.9.3",
|
|
80
|
-
"vitest": "^4.
|
|
84
|
+
"vitest": "^4.1.8"
|
|
81
85
|
},
|
|
82
86
|
"overrides": {
|
|
83
|
-
"lodash": "^4.18.0"
|
|
87
|
+
"lodash": "^4.18.0",
|
|
88
|
+
"axios": "^1.17.0"
|
|
84
89
|
}
|
|
85
90
|
}
|
package/skill/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Bitcoin L1 wallet for agents - check balances, send BTC, manage UTX
|
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
6
|
author: aibtcdev
|
|
7
|
-
version: 1.
|
|
7
|
+
version: 1.59.0 # x-release-please-version
|
|
8
8
|
npm: "@aibtc/mcp-server"
|
|
9
9
|
github: https://github.com/aibtcdev/aibtc-mcp-server
|
|
10
10
|
---
|
|
@@ -15,13 +15,24 @@ A skill for managing Bitcoin L1 wallets with optional Pillar smart wallet and St
|
|
|
15
15
|
|
|
16
16
|
## Install
|
|
17
17
|
|
|
18
|
-
One-command installation:
|
|
18
|
+
One-command installation (Claude Code is the default):
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
npx @aibtc/mcp-server@latest --install
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Other MCP clients are selected with a flag:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx @aibtc/mcp-server@latest --install --cursor # Cursor
|
|
28
|
+
npx @aibtc/mcp-server@latest --install --codex # OpenAI Codex CLI
|
|
29
|
+
npx @aibtc/mcp-server@latest --install --gemini # Gemini CLI
|
|
30
|
+
npx @aibtc/mcp-server@latest --install --windsurf # Windsurf
|
|
31
|
+
npx @aibtc/mcp-server@latest --install --vscode # VS Code
|
|
32
|
+
npx @aibtc/mcp-server@latest --install --desktop # Claude Desktop
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
For testnet, add `--testnet` to any command:
|
|
25
36
|
|
|
26
37
|
```bash
|
|
27
38
|
npx @aibtc/mcp-server@latest --install --testnet
|