@aibtc/mcp-server 1.70.1 → 1.72.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +45 -8
  2. package/dist/bridge/index.js +4 -4
  3. package/dist/config/contracts.d.ts +4 -4
  4. package/dist/config/contracts.js +2 -2
  5. package/dist/config/networks.js +4 -1
  6. package/dist/endpoints/registry.d.ts +8 -26
  7. package/dist/endpoints/registry.js +7 -932
  8. package/dist/index.js +118 -42
  9. package/dist/services/stacking.service.d.ts +144 -45
  10. package/dist/services/stacking.service.js +416 -123
  11. package/dist/services/x402-discovery.service.d.ts +81 -0
  12. package/dist/services/x402-discovery.service.js +170 -0
  13. package/dist/tools/arxiv-research.tools.d.ts +1 -1
  14. package/dist/tools/arxiv-research.tools.js +6 -6
  15. package/dist/tools/at-stake-legion.tools.d.ts +1 -1
  16. package/dist/tools/at-stake-legion.tools.js +14 -14
  17. package/dist/tools/at-stake.tools.d.ts +1 -1
  18. package/dist/tools/at-stake.tools.js +17 -17
  19. package/dist/tools/bitcoin.tools.d.ts +1 -1
  20. package/dist/tools/bitcoin.tools.js +12 -12
  21. package/dist/tools/bitflow.tools.d.ts +1 -1
  22. package/dist/tools/bitflow.tools.js +21 -21
  23. package/dist/tools/bns.tools.d.ts +1 -1
  24. package/dist/tools/bns.tools.js +18 -18
  25. package/dist/tools/bounty-hint.d.ts +1 -1
  26. package/dist/tools/bounty-scanner.tools.d.ts +1 -1
  27. package/dist/tools/bounty-scanner.tools.js +20 -20
  28. package/dist/tools/child-inscription.tools.d.ts +1 -1
  29. package/dist/tools/child-inscription.tools.js +6 -6
  30. package/dist/tools/competition.tools.d.ts +1 -1
  31. package/dist/tools/competition.tools.js +4 -4
  32. package/dist/tools/contract.tools.d.ts +1 -1
  33. package/dist/tools/contract.tools.js +8 -8
  34. package/dist/tools/credentials.tools.d.ts +1 -1
  35. package/dist/tools/credentials.tools.js +8 -8
  36. package/dist/tools/defi.tools.d.ts +1 -1
  37. package/dist/tools/defi.tools.js +21 -21
  38. package/dist/tools/dual-stacking.tools.d.ts +1 -1
  39. package/dist/tools/dual-stacking.tools.js +7 -7
  40. package/dist/tools/earning.tools.d.ts +1 -1
  41. package/dist/tools/earning.tools.js +5 -5
  42. package/dist/tools/endpoint.tools.d.ts +1 -1
  43. package/dist/tools/endpoint.tools.js +92 -49
  44. package/dist/tools/identity.tools.d.ts +1 -1
  45. package/dist/tools/identity.tools.js +19 -19
  46. package/dist/tools/inbox-x402.tools.d.ts +1 -1
  47. package/dist/tools/inbox-x402.tools.js +2 -2
  48. package/dist/tools/inbox.tools.d.ts +1 -1
  49. package/dist/tools/inbox.tools.js +2 -2
  50. package/dist/tools/index.d.ts +6 -3
  51. package/dist/tools/index.js +69 -58
  52. package/dist/tools/inference-marketplace.tools.d.ts +1 -1
  53. package/dist/tools/inference-marketplace.tools.js +10 -10
  54. package/dist/tools/jingswap.tools.d.ts +1 -1
  55. package/dist/tools/jingswap.tools.js +30 -30
  56. package/dist/tools/legion.tools.d.ts +1 -1
  57. package/dist/tools/legion.tools.js +20 -20
  58. package/dist/tools/lightning.tools.d.ts +1 -1
  59. package/dist/tools/lightning.tools.js +14 -14
  60. package/dist/tools/mempool.tools.d.ts +1 -1
  61. package/dist/tools/mempool.tools.js +5 -5
  62. package/dist/tools/news.tools.d.ts +1 -1
  63. package/dist/tools/news.tools.js +2 -1
  64. package/dist/tools/nft.tools.d.ts +1 -1
  65. package/dist/tools/nft.tools.js +12 -12
  66. package/dist/tools/nonce.tools.d.ts +1 -1
  67. package/dist/tools/nonce.tools.js +8 -8
  68. package/dist/tools/nostr.tools.d.ts +1 -1
  69. package/dist/tools/nostr.tools.js +12 -12
  70. package/dist/tools/openrouter.tools.d.ts +1 -1
  71. package/dist/tools/openrouter.tools.js +4 -4
  72. package/dist/tools/ordinals-marketplace.tools.d.ts +1 -1
  73. package/dist/tools/ordinals-marketplace.tools.js +10 -10
  74. package/dist/tools/ordinals-p2p.tools.d.ts +1 -1
  75. package/dist/tools/ordinals-p2p.tools.js +18 -18
  76. package/dist/tools/ordinals.tools.d.ts +1 -1
  77. package/dist/tools/ordinals.tools.js +8 -8
  78. package/dist/tools/pillar-direct.tools.d.ts +1 -1
  79. package/dist/tools/pillar-direct.tools.js +59 -38
  80. package/dist/tools/pillar.tools.d.ts +1 -1
  81. package/dist/tools/pillar.tools.js +28 -26
  82. package/dist/tools/profiles.d.ts +51 -0
  83. package/dist/tools/profiles.js +111 -0
  84. package/dist/tools/psbt.tools.d.ts +1 -1
  85. package/dist/tools/psbt.tools.js +8 -8
  86. package/dist/tools/query.tools.d.ts +1 -1
  87. package/dist/tools/query.tools.js +12 -12
  88. package/dist/tools/relay-diagnostic.tools.d.ts +1 -1
  89. package/dist/tools/relay-diagnostic.tools.js +3 -3
  90. package/dist/tools/reputation.tools.d.ts +1 -1
  91. package/dist/tools/reputation.tools.js +22 -22
  92. package/dist/tools/runes.tools.d.ts +1 -1
  93. package/dist/tools/runes.tools.js +18 -18
  94. package/dist/tools/sbtc.tools.d.ts +1 -1
  95. package/dist/tools/sbtc.tools.js +14 -33
  96. package/dist/tools/scaffold.tools.d.ts +1 -1
  97. package/dist/tools/scaffold.tools.js +4 -4
  98. package/dist/tools/settings.tools.d.ts +1 -1
  99. package/dist/tools/settings.tools.js +9 -9
  100. package/dist/tools/signing.tools.d.ts +1 -1
  101. package/dist/tools/signing.tools.js +20 -20
  102. package/dist/tools/skill-mappings.js +4 -0
  103. package/dist/tools/souldinals.tools.d.ts +1 -1
  104. package/dist/tools/souldinals.tools.js +10 -10
  105. package/dist/tools/stacking-lottery.tools.d.ts +1 -1
  106. package/dist/tools/stacking-lottery.tools.js +11 -11
  107. package/dist/tools/stacking.tools.d.ts +1 -1
  108. package/dist/tools/stacking.tools.js +358 -50
  109. package/dist/tools/stacks-market.tools.d.ts +1 -1
  110. package/dist/tools/stacks-market.tools.js +22 -22
  111. package/dist/tools/styx.tools.d.ts +1 -1
  112. package/dist/tools/styx.tools.js +11 -11
  113. package/dist/tools/taproot-multisig.tools.d.ts +1 -1
  114. package/dist/tools/taproot-multisig.tools.js +5 -5
  115. package/dist/tools/tenero.tools.d.ts +1 -1
  116. package/dist/tools/tenero.tools.js +22 -22
  117. package/dist/tools/tokens.tools.d.ts +1 -1
  118. package/dist/tools/tokens.tools.js +10 -10
  119. package/dist/tools/transfer.tools.d.ts +1 -1
  120. package/dist/tools/transfer.tools.js +4 -4
  121. package/dist/tools/wallet-management.tools.d.ts +1 -1
  122. package/dist/tools/wallet-management.tools.js +16 -16
  123. package/dist/tools/wallet.tools.d.ts +1 -1
  124. package/dist/tools/wallet.tools.js +2 -2
  125. package/dist/tools/yield-dashboard.tools.d.ts +1 -1
  126. package/dist/tools/yield-dashboard.tools.js +18 -27
  127. package/dist/tools/yield-hunter.tools.d.ts +1 -1
  128. package/dist/tools/yield-hunter.tools.js +6 -6
  129. package/dist/utils/bitcoin.d.ts +8 -0
  130. package/dist/utils/bitcoin.js +24 -0
  131. package/dist/utils/fee.js +1 -1
  132. package/package.json +8 -7
  133. package/skill/SKILL.md +1 -1
  134. package/skill/references/stacks-defi.md +1 -1
@@ -1,107 +1,415 @@
1
1
  import { z } from "zod";
2
2
  import { getAccount, getWalletAddress, NETWORK } from "../services/x402.service.js";
3
- import { getStackingService } from "../services/stacking.service.js";
3
+ import { MAX_STAKE_CYCLES, buildPayoutCalldata, getStackingService, } from "../services/stacking.service.js";
4
4
  import { getExplorerTxUrl } from "../config/networks.js";
5
5
  import { createJsonResponse, createErrorResponse } from "../utils/index.js";
6
+ import { btcAddressToPoxAddr } from "../utils/bitcoin.js";
7
+ const POX5_NOTE = "PoX-5: every stake names a signer manager contract; rewards are paid in sBTC through that manager.";
8
+ function stx(ustx) {
9
+ const whole = ustx / 1000000n;
10
+ const frac = (ustx % 1000000n).toString().padStart(6, "0");
11
+ return `${whole}.${frac} STX`;
12
+ }
13
+ function poxSummary(pox) {
14
+ return {
15
+ contract: pox.contractId,
16
+ burnHeight: pox.burnHeight,
17
+ rewardCycle: pox.rewardCycle,
18
+ nextCycleStartHeight: pox.nextCycleStartHeight,
19
+ preparePhaseStartHeight: pox.preparePhaseStartHeight,
20
+ inPreparePhase: pox.inPreparePhase,
21
+ };
22
+ }
23
+ function stakerSummary(info) {
24
+ return {
25
+ signerManager: info.signerManager,
26
+ amountUstx: info.amountUstx.toString(),
27
+ amount: stx(info.amountUstx),
28
+ firstRewardCycle: info.firstRewardCycle,
29
+ numCycles: info.numCycles,
30
+ unlockCycle: info.unlockCycle,
31
+ unlockBurnHeight: info.unlockBurnHeight,
32
+ };
33
+ }
34
+ const payoutInputs = {
35
+ btcRewardAddress: z
36
+ .string()
37
+ .optional()
38
+ .describe("Optional Bitcoin address to receive rewards. Encoded as `{ pox-addr, max-fee }` signer calldata, " +
39
+ "the shape reference signer managers (e.g. Xverse, Fast Pool) use to pay rewards to BTC via an " +
40
+ "sBTC withdrawal. Some managers require it (PlanBetter), some ignore it (native-pool). " +
41
+ "Omit to receive sBTC where the manager supports that."),
42
+ maxWithdrawalFeeSats: z
43
+ .number()
44
+ .int()
45
+ .nonnegative()
46
+ .optional()
47
+ .describe("Max sBTC withdrawal fee (sats) per payout when btcRewardAddress is set. Default 3000."),
48
+ signerCalldataHex: z
49
+ .string()
50
+ .optional()
51
+ .describe("Advanced: raw signer calldata (hex, max 500 bytes) for managers with a custom format. " +
52
+ "Cannot be combined with btcRewardAddress."),
53
+ };
54
+ function resolveCalldata(input) {
55
+ if (input.btcRewardAddress && input.signerCalldataHex) {
56
+ throw new Error("Pass either btcRewardAddress or signerCalldataHex, not both.");
57
+ }
58
+ if (input.btcRewardAddress) {
59
+ return buildPayoutCalldata(btcAddressToPoxAddr(input.btcRewardAddress, NETWORK), BigInt(input.maxWithdrawalFeeSats ?? 3000));
60
+ }
61
+ if (input.signerCalldataHex) {
62
+ const hex = input.signerCalldataHex.replace(/^0x/, "");
63
+ if (!/^([0-9a-fA-F]{2})*$/.test(hex)) {
64
+ throw new Error("signerCalldataHex must be an even-length hex string.");
65
+ }
66
+ return Uint8Array.from(Buffer.from(hex, "hex"));
67
+ }
68
+ return undefined;
69
+ }
6
70
  export function registerStackingTools(server) {
7
- // Get PoX info
71
+ // --- get_pox_info ---
8
72
  server.registerTool("get_pox_info", {
9
- description: "Get current Proof of Transfer (PoX) cycle information.",
73
+ description: "Current PoX-5 state: reward cycle, burn height, next cycle start, and whether the prepare " +
74
+ "phase is active (stake, extend and unstake are refused during it).",
10
75
  }, async () => {
11
76
  try {
12
- const stackingService = getStackingService(NETWORK);
13
- const poxInfo = await stackingService.getPoxInfo();
77
+ const pox = await getStackingService(NETWORK).getPoxState();
14
78
  return createJsonResponse({
15
79
  network: NETWORK,
16
- currentCycle: poxInfo.current_cycle,
17
- nextCycle: poxInfo.next_cycle,
18
- minAmountUstx: poxInfo.min_amount_ustx,
19
- rewardCycleLength: poxInfo.reward_cycle_length,
20
- prepareCycleLength: poxInfo.prepare_cycle_length,
21
- currentBurnchainBlockHeight: poxInfo.current_burnchain_block_height,
22
- totalLiquidSupplyUstx: poxInfo.total_liquid_supply_ustx,
80
+ ...poxSummary(pox),
81
+ firstBurnHeight: pox.firstBurnHeight,
82
+ rewardCycleLength: pox.rewardCycleLength,
83
+ prepareCycleLength: pox.prepareCycleLength,
84
+ signerSetMinUstx: pox.signerSetMinUstx.toString(),
85
+ totalLiquidSupplyUstx: pox.totalLiquidSupplyUstx.toString(),
86
+ note: POX5_NOTE,
23
87
  });
24
88
  }
25
89
  catch (error) {
26
90
  return createErrorResponse(error);
27
91
  }
28
92
  });
29
- // Get stacking status
93
+ // --- get_stacking_status ---
30
94
  server.registerTool("get_stacking_status", {
31
- description: "Check if an address is currently stacking STX.",
32
- inputSchema: {
95
+ description: "PoX-5 staking status for an address: locked amount, signer manager, lock period, unlock " +
96
+ "cycle and burn height, any protocol bond membership, and locked/unlocked STX balance.",
97
+ inputSchema: z.object({
33
98
  address: z
34
99
  .string()
35
100
  .optional()
36
- .describe("Wallet address to check. Uses configured wallet if not provided."),
37
- },
101
+ .describe("Stacks address to check. Uses the active wallet if omitted."),
102
+ }),
38
103
  }, async ({ address }) => {
39
104
  try {
40
- const stackingService = getStackingService(NETWORK);
41
105
  const walletAddress = address || (await getWalletAddress());
42
- const status = await stackingService.getStackingStatus(walletAddress);
106
+ const status = await getStackingService(NETWORK).getStakingStatus(walletAddress);
43
107
  return createJsonResponse({
44
108
  address: walletAddress,
45
109
  network: NETWORK,
46
- ...status,
110
+ staking: status.staking !== null,
111
+ stake: status.staking ? stakerSummary(status.staking) : null,
112
+ bond: status.bond
113
+ ? {
114
+ bondIndex: status.bond.bondIndex,
115
+ signerManager: status.bond.signerManager,
116
+ amountUstx: status.bond.amountUstx.toString(),
117
+ amountSats: status.bond.amountSats.toString(),
118
+ isL1Lock: status.bond.isL1Lock,
119
+ }
120
+ : null,
121
+ balance: {
122
+ totalUstx: status.account.balanceUstx.toString(),
123
+ lockedUstx: status.account.lockedUstx.toString(),
124
+ unlockedUstx: status.account.unlockedUstx.toString(),
125
+ burnchainUnlockHeight: status.account.burnchainUnlockHeight,
126
+ },
127
+ pox: poxSummary(status.pox),
47
128
  });
48
129
  }
49
130
  catch (error) {
50
131
  return createErrorResponse(error);
51
132
  }
52
133
  });
53
- // Stack STX
134
+ // --- list_stacking_signers ---
135
+ server.registerTool("list_stacking_signers", {
136
+ description: "List PoX-5 signer managers in the signer set for a reward cycle (default: next cycle), with " +
137
+ "the STX delegated to each. A signer manager is what stack_stx stakes with. Only signers with " +
138
+ "at least the signer-set minimum delegated appear here; other registered managers can still be " +
139
+ "staked with by contract id. Set withPayoutInfo to see how each manager pays stakers (slower).",
140
+ inputSchema: z.object({
141
+ rewardCycle: z
142
+ .number()
143
+ .int()
144
+ .positive()
145
+ .optional()
146
+ .describe("Reward cycle to list. Defaults to the next cycle (the one a new stake joins)."),
147
+ withPayoutInfo: z
148
+ .boolean()
149
+ .optional()
150
+ .default(false)
151
+ .describe("Also read each manager's claim path (one extra request per signer)."),
152
+ }),
153
+ }, async ({ rewardCycle, withPayoutInfo }) => {
154
+ try {
155
+ const service = getStackingService(NETWORK);
156
+ const pox = await service.getPoxState();
157
+ const cycle = rewardCycle ?? pox.rewardCycle + 1;
158
+ const signers = (await service.getSignerSet(cycle)).sort((a, b) => b.delegatedUstx > a.delegatedUstx ? 1 : b.delegatedUstx < a.delegatedUstx ? -1 : 0);
159
+ const rows = [];
160
+ for (const s of signers) {
161
+ rows.push({
162
+ signerManager: s.signerManager,
163
+ delegatedUstx: s.delegatedUstx.toString(),
164
+ delegated: stx(s.delegatedUstx),
165
+ ...(withPayoutInfo && { stakerClaim: await service.getClaimStyle(s.signerManager) }),
166
+ });
167
+ }
168
+ return createJsonResponse({
169
+ network: NETWORK,
170
+ rewardCycle: cycle,
171
+ count: rows.length,
172
+ signers: rows,
173
+ ...(withPayoutInfo && {
174
+ stakerClaimKey: {
175
+ "staker-arg": "on-chain; claim_stacking_rewards works",
176
+ caller: "on-chain; claim_stacking_rewards works (paid to the caller)",
177
+ none: "no on-chain staker claim; the manager pays off-chain or not at all",
178
+ },
179
+ }),
180
+ note: "Managers can restrict who may stake (allowlists, minimums, required payout calldata). " +
181
+ "Check a manager's own terms before staking; a refused stake aborts on chain.",
182
+ });
183
+ }
184
+ catch (error) {
185
+ return createErrorResponse(error);
186
+ }
187
+ });
188
+ // --- stack_stx ---
54
189
  server.registerTool("stack_stx", {
55
- description: `Lock STX for stacking to earn BTC rewards.
190
+ description: `Lock STX in PoX-5 with a signer manager to earn sBTC rewards.
56
191
 
57
- Requires a Bitcoin address (hash) for receiving rewards.`,
58
- inputSchema: {
59
- amount: z.string().describe("Amount of STX to stack (in micro-STX)"),
60
- poxAddressVersion: z.number().describe("Bitcoin address version (0 for P2PKH, 1 for P2SH, etc.)"),
61
- poxAddressHashbytes: z.string().describe("Bitcoin address hash (hex string)"),
62
- startBurnHeight: z.number().describe("Bitcoin block height to start stacking"),
63
- lockPeriod: z.number().min(1).max(12).describe("Number of reward cycles to lock (1-12)"),
64
- },
65
- }, async ({ amount, poxAddressVersion, poxAddressHashbytes, startBurnHeight, lockPeriod }) => {
192
+ The lock starts next reward cycle and lasts numCycles cycles (1-${MAX_STAKE_CYCLES}). Pick a signer manager with list_stacking_signers. Refused during the prepare phase and if the address is already staking (use extend_stacking). Locking is not a transfer: the transaction carries a staking post-condition for exactly the locked amount.`,
193
+ inputSchema: z.object({
194
+ signerManager: z
195
+ .string()
196
+ .describe("Signer manager contract id, e.g. SP8HK160YD5GHXP69VGA0TC7AQJ1X4CDW3XVERSE.xverse-signer-manager-2"),
197
+ amount: z.string().describe("Amount to lock, in micro-STX"),
198
+ numCycles: z
199
+ .number()
200
+ .int()
201
+ .min(1)
202
+ .max(MAX_STAKE_CYCLES)
203
+ .describe(`Reward cycles to lock (1-${MAX_STAKE_CYCLES}; one cycle is about two weeks)`),
204
+ ...payoutInputs,
205
+ }),
206
+ }, async ({ signerManager, amount, numCycles, ...payout }) => {
66
207
  try {
67
- const stackingService = getStackingService(NETWORK);
68
208
  const account = await getAccount();
69
- const result = await stackingService.stack(account, BigInt(amount), { version: poxAddressVersion, hashbytes: poxAddressHashbytes }, startBurnHeight, lockPeriod);
209
+ const result = await getStackingService(NETWORK).stake(account, {
210
+ signerManager,
211
+ amountUstx: BigInt(amount),
212
+ numCycles,
213
+ signerCalldata: resolveCalldata(payout),
214
+ });
70
215
  return createJsonResponse({
71
216
  success: true,
72
217
  txid: result.txid,
73
- stacker: account.address,
74
- amount,
75
- lockPeriod,
76
- startBurnHeight,
77
- network: NETWORK,
78
218
  explorerUrl: getExplorerTxUrl(result.txid, NETWORK),
219
+ staker: account.address,
220
+ signerManager,
221
+ amountUstx: amount,
222
+ amount: stx(BigInt(amount)),
223
+ firstRewardCycle: result.pox.rewardCycle + 1,
224
+ numCycles,
225
+ unlockCycle: result.unlockCycle,
226
+ unlockBurnHeight: result.unlockBurnHeight,
227
+ rewardPayout: payout.btcRewardAddress
228
+ ? `BTC to ${payout.btcRewardAddress} (if the manager supports it)`
229
+ : payout.signerCalldataHex
230
+ ? "custom signer calldata"
231
+ : "sBTC (if the manager supports it)",
232
+ network: NETWORK,
79
233
  });
80
234
  }
81
235
  catch (error) {
82
236
  return createErrorResponse(error);
83
237
  }
84
238
  });
85
- // Extend stacking
239
+ // --- extend_stacking ---
86
240
  server.registerTool("extend_stacking", {
87
- description: "Extend an existing stacking lock period.",
88
- inputSchema: {
89
- extendCount: z.number().min(1).max(12).describe("Number of additional cycles to lock"),
90
- poxAddressVersion: z.number().describe("Bitcoin address version"),
91
- poxAddressHashbytes: z.string().describe("Bitcoin address hash (hex string)"),
92
- },
93
- }, async ({ extendCount, poxAddressVersion, poxAddressHashbytes }) => {
241
+ description: `Update an existing PoX-5 stake: extend the lock, add STX, switch signer manager, or change payout calldata (pox-5 stake-update).
242
+
243
+ Any combination in one call. Increasing locks more of the address's unlocked STX. Refused during the prepare phase. The lock may not run more than ${MAX_STAKE_CYCLES} cycles past the next cycle.`,
244
+ inputSchema: z.object({
245
+ cyclesToExtend: z
246
+ .number()
247
+ .int()
248
+ .nonnegative()
249
+ .optional()
250
+ .default(0)
251
+ .describe("Cycles to add to the current unlock cycle"),
252
+ amountIncrease: z
253
+ .string()
254
+ .optional()
255
+ .default("0")
256
+ .describe("Additional micro-STX to lock"),
257
+ signerManager: z
258
+ .string()
259
+ .optional()
260
+ .describe("New signer manager contract id. Defaults to the current one."),
261
+ ...payoutInputs,
262
+ }),
263
+ }, async ({ cyclesToExtend, amountIncrease, signerManager, ...payout }) => {
94
264
  try {
95
- const stackingService = getStackingService(NETWORK);
96
265
  const account = await getAccount();
97
- const result = await stackingService.extendStacking(account, extendCount, { version: poxAddressVersion, hashbytes: poxAddressHashbytes });
266
+ const result = await getStackingService(NETWORK).updateStake(account, {
267
+ signerManager,
268
+ cyclesToExtend,
269
+ amountIncreaseUstx: BigInt(amountIncrease),
270
+ signerCalldata: resolveCalldata(payout),
271
+ });
98
272
  return createJsonResponse({
99
273
  success: true,
100
274
  txid: result.txid,
101
- stacker: account.address,
102
- extendCount,
275
+ explorerUrl: getExplorerTxUrl(result.txid, NETWORK),
276
+ staker: account.address,
277
+ previous: stakerSummary(result.previous),
278
+ signerManager: result.signerManager,
279
+ newAmountUstx: result.newAmountUstx.toString(),
280
+ newAmount: stx(result.newAmountUstx),
281
+ unlockCycle: result.unlockCycle,
282
+ unlockBurnHeight: result.unlockBurnHeight,
103
283
  network: NETWORK,
284
+ });
285
+ }
286
+ catch (error) {
287
+ return createErrorResponse(error);
288
+ }
289
+ });
290
+ // --- unstake_stx ---
291
+ server.registerTool("unstake_stx", {
292
+ description: "Stop a PoX-5 stake early (pox-5 unstake). The STX stays locked through the current reward " +
293
+ "cycle and unlocks at the start of the next one. Refused during the prepare phase.",
294
+ inputSchema: z.object({}),
295
+ }, async () => {
296
+ try {
297
+ const account = await getAccount();
298
+ const result = await getStackingService(NETWORK).unstake(account);
299
+ return createJsonResponse({
300
+ success: true,
301
+ txid: result.txid,
104
302
  explorerUrl: getExplorerTxUrl(result.txid, NETWORK),
303
+ staker: account.address,
304
+ previous: stakerSummary(result.previous),
305
+ unlockCycle: result.unlockCycle,
306
+ unlockBurnHeight: result.unlockBurnHeight,
307
+ network: NETWORK,
308
+ });
309
+ }
310
+ catch (error) {
311
+ return createErrorResponse(error);
312
+ }
313
+ });
314
+ // --- get_stacking_rewards ---
315
+ server.registerTool("get_stacking_rewards", {
316
+ description: "PoX-5 sBTC rewards an address has earned from its signer manager for one reward cycle and not " +
317
+ "yet claimed (before the manager's fees), whether the manager still has to pull them from pox-5, " +
318
+ "and whether the manager supports an on-chain staker claim.",
319
+ inputSchema: z.object({
320
+ rewardCycle: z.number().int().nonnegative().describe("Reward cycle to check"),
321
+ address: z
322
+ .string()
323
+ .optional()
324
+ .describe("Stacks address. Uses the active wallet if omitted."),
325
+ signerManager: z
326
+ .string()
327
+ .optional()
328
+ .describe("Signer manager to check. Defaults to the address's current signer manager."),
329
+ }),
330
+ }, async ({ rewardCycle, address, signerManager }) => {
331
+ try {
332
+ const service = getStackingService(NETWORK);
333
+ const staker = address || (await getWalletAddress());
334
+ const manager = signerManager ?? (await service.getStakerInfo(staker))?.signerManager;
335
+ if (!manager) {
336
+ throw new Error(`${staker} is not currently staking; pass signerManager to check rewards from a past signer.`);
337
+ }
338
+ const [earnedSats, unpulledSats, claimStyle] = await Promise.all([
339
+ service.getStakerUnclaimedRewards(manager, rewardCycle, staker),
340
+ service.getSignerUnpulledRewards(manager, rewardCycle),
341
+ service.getClaimStyle(manager),
342
+ ]);
343
+ return createJsonResponse({
344
+ address: staker,
345
+ network: NETWORK,
346
+ rewardCycle,
347
+ signerManager: manager,
348
+ unclaimedSatsBeforeFees: earnedSats.toString(),
349
+ managerUnpulledSats: unpulledSats.toString(),
350
+ stakerClaim: claimStyle,
351
+ next: earnedSats === 0n
352
+ ? "Nothing to claim for this cycle."
353
+ : claimStyle === "none"
354
+ ? "This manager has no on-chain staker claim; it pays stakers off-chain."
355
+ : "Call claim_stacking_rewards with this rewardCycle.",
356
+ });
357
+ }
358
+ catch (error) {
359
+ return createErrorResponse(error);
360
+ }
361
+ });
362
+ // --- claim_stacking_rewards ---
363
+ server.registerTool("claim_stacking_rewards", {
364
+ description: "Claim PoX-5 sBTC rewards for one reward cycle through the staker's signer manager. If the " +
365
+ "manager has not yet pulled that cycle's rewards from pox-5, this first sends the manager's " +
366
+ "permissionless claim-rewards (a second transaction, paid by this wallet), then the staker " +
367
+ "claim. Rewards arrive as sBTC, or as a BTC withdrawal if a payout address was set when " +
368
+ "staking. Not available for managers that pay off-chain.",
369
+ inputSchema: z.object({
370
+ rewardCycle: z.number().int().nonnegative().describe("Reward cycle to claim"),
371
+ signerManager: z
372
+ .string()
373
+ .optional()
374
+ .describe("Signer manager to claim from. Defaults to the wallet's current signer manager."),
375
+ }),
376
+ }, async ({ rewardCycle, signerManager }) => {
377
+ try {
378
+ const service = getStackingService(NETWORK);
379
+ const account = await getAccount();
380
+ const manager = signerManager ?? (await service.getStakerInfo(account.address))?.signerManager;
381
+ if (!manager) {
382
+ throw new Error(`${account.address} is not currently staking; pass signerManager to claim from a past signer.`);
383
+ }
384
+ const [earnedSats, unpulledSats, claimStyle] = await Promise.all([
385
+ service.getStakerUnclaimedRewards(manager, rewardCycle, account.address),
386
+ service.getSignerUnpulledRewards(manager, rewardCycle),
387
+ service.getClaimStyle(manager),
388
+ ]);
389
+ if (claimStyle === "none") {
390
+ throw new Error(`${manager} has no on-chain staker claim (claim-staker-rewards); it pays stakers off-chain.`);
391
+ }
392
+ if (earnedSats === 0n) {
393
+ throw new Error(`No unclaimed rewards for ${account.address} from ${manager} in cycle ${rewardCycle}.`);
394
+ }
395
+ let pull = null;
396
+ if (unpulledSats > 0n) {
397
+ const pulled = await service.pullSignerRewards(account, manager, rewardCycle, unpulledSats);
398
+ pull = { txid: pulled.txid, explorerUrl: getExplorerTxUrl(pulled.txid, NETWORK) };
399
+ }
400
+ const claim = await service.claimStakerRewards(account, manager, rewardCycle, claimStyle);
401
+ return createJsonResponse({
402
+ success: true,
403
+ network: NETWORK,
404
+ rewardCycle,
405
+ signerManager: manager,
406
+ unclaimedSatsBeforeFees: earnedSats.toString(),
407
+ managerPull: pull,
408
+ txid: claim.txid,
409
+ explorerUrl: getExplorerTxUrl(claim.txid, NETWORK),
410
+ note: pull
411
+ ? "Two transactions were sent in nonce order: the manager's pull, then the staker claim."
412
+ : undefined,
105
413
  });
106
414
  }
107
415
  catch (error) {
@@ -17,6 +17,6 @@
17
17
  * - stacks_market_redeem — Redeem winning shares after market resolution
18
18
  * - stacks_market_get_position — Check YES/NO share balances for any address
19
19
  */
20
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
20
+ import { McpServer } from "@modelcontextprotocol/server";
21
21
  export declare function registerStacksMarketTools(server: McpServer): void;
22
22
  //# sourceMappingURL=stacks-market.tools.d.ts.map
@@ -85,7 +85,7 @@ export function registerStacksMarketTools(server) {
85
85
  Returns a paginated list of markets with optional filtering by status, category, or featured flag.
86
86
 
87
87
  Note: Stacks Market is only available on mainnet.`,
88
- inputSchema: {
88
+ inputSchema: z.object({
89
89
  limit: z
90
90
  .number()
91
91
  .optional()
@@ -103,7 +103,7 @@ Note: Stacks Market is only available on mainnet.`,
103
103
  .boolean()
104
104
  .optional()
105
105
  .describe("When true, return only featured markets"),
106
- },
106
+ }),
107
107
  }, async ({ limit, status, category, featured }) => {
108
108
  try {
109
109
  if (NETWORK !== "mainnet") {
@@ -137,14 +137,14 @@ Note: Stacks Market is only available on mainnet.`,
137
137
  Searches across market titles and descriptions.
138
138
 
139
139
  Note: Stacks Market is only available on mainnet.`,
140
- inputSchema: {
140
+ inputSchema: z.object({
141
141
  query: z.string().describe("Search keyword or phrase"),
142
142
  limit: z
143
143
  .number()
144
144
  .optional()
145
145
  .default(20)
146
146
  .describe("Maximum number of results to return (default 20)"),
147
- },
147
+ }),
148
148
  }, async ({ query, limit }) => {
149
149
  try {
150
150
  if (NETWORK !== "mainnet") {
@@ -172,11 +172,11 @@ Note: Stacks Market is only available on mainnet.`,
172
172
  Market IDs are epoch millisecond timestamps (uint) visible in market URLs.
173
173
 
174
174
  Note: Stacks Market is only available on mainnet.`,
175
- inputSchema: {
175
+ inputSchema: z.object({
176
176
  market_id: z
177
177
  .string()
178
178
  .describe("Market ID (epoch ms timestamp, e.g. '1710000000000')"),
179
- },
179
+ }),
180
180
  }, async ({ market_id }) => {
181
181
  try {
182
182
  if (NETWORK !== "mainnet") {
@@ -202,7 +202,7 @@ Returns the cost in micro-STX to buy the requested number of shares.
202
202
  Side: 0 = YES, 1 = NO.
203
203
 
204
204
  Note: Stacks Market is only available on mainnet.`,
205
- inputSchema: {
205
+ inputSchema: z.object({
206
206
  market_id: z
207
207
  .string()
208
208
  .describe("Market ID (epoch ms timestamp)"),
@@ -214,7 +214,7 @@ Note: Stacks Market is only available on mainnet.`,
214
214
  .min(0)
215
215
  .max(1)
216
216
  .describe("0 = YES shares, 1 = NO shares"),
217
- },
217
+ }),
218
218
  }, async ({ market_id, shares, side }) => {
219
219
  try {
220
220
  if (NETWORK !== "mainnet") {
@@ -252,7 +252,7 @@ Returns the proceeds in micro-STX for selling the requested number of shares.
252
252
  Side: 0 = YES, 1 = NO.
253
253
 
254
254
  Note: Stacks Market is only available on mainnet.`,
255
- inputSchema: {
255
+ inputSchema: z.object({
256
256
  market_id: z
257
257
  .string()
258
258
  .describe("Market ID (epoch ms timestamp)"),
@@ -264,7 +264,7 @@ Note: Stacks Market is only available on mainnet.`,
264
264
  .min(0)
265
265
  .max(1)
266
266
  .describe("0 = YES shares, 1 = NO shares"),
267
- },
267
+ }),
268
268
  }, async ({ market_id, shares, side }) => {
269
269
  try {
270
270
  if (NETWORK !== "mainnet") {
@@ -302,7 +302,7 @@ Uses the buy-yes-auto function with slippage protection via a max-cost cap.
302
302
  The transaction will fail if the cost exceeds max_cost_ustx.
303
303
 
304
304
  Requires an unlocked wallet. Only available on mainnet.`,
305
- inputSchema: {
305
+ inputSchema: z.object({
306
306
  market_id: z
307
307
  .string()
308
308
  .describe("Market ID (epoch ms timestamp)"),
@@ -315,7 +315,7 @@ Requires an unlocked wallet. Only available on mainnet.`,
315
315
  max_cost_ustx: z
316
316
  .string()
317
317
  .describe("Maximum cost in micro-STX you are willing to pay (slippage protection)"),
318
- },
318
+ }),
319
319
  }, async ({ market_id, shares, target_cap_ustx, max_cost_ustx }) => {
320
320
  try {
321
321
  if (NETWORK !== "mainnet") {
@@ -362,7 +362,7 @@ Uses the buy-no-auto function with slippage protection via a max-cost cap.
362
362
  The transaction will fail if the cost exceeds max_cost_ustx.
363
363
 
364
364
  Requires an unlocked wallet. Only available on mainnet.`,
365
- inputSchema: {
365
+ inputSchema: z.object({
366
366
  market_id: z
367
367
  .string()
368
368
  .describe("Market ID (epoch ms timestamp)"),
@@ -375,7 +375,7 @@ Requires an unlocked wallet. Only available on mainnet.`,
375
375
  max_cost_ustx: z
376
376
  .string()
377
377
  .describe("Maximum cost in micro-STX you are willing to pay (slippage protection)"),
378
- },
378
+ }),
379
379
  }, async ({ market_id, shares, target_cap_ustx, max_cost_ustx }) => {
380
380
  try {
381
381
  if (NETWORK !== "mainnet") {
@@ -422,7 +422,7 @@ Uses the sell-yes-auto function with a minimum proceeds guard.
422
422
  The transaction will fail if proceeds fall below min_proceeds_ustx.
423
423
 
424
424
  Requires an unlocked wallet. Only available on mainnet.`,
425
- inputSchema: {
425
+ inputSchema: z.object({
426
426
  market_id: z
427
427
  .string()
428
428
  .describe("Market ID (epoch ms timestamp)"),
@@ -432,7 +432,7 @@ Requires an unlocked wallet. Only available on mainnet.`,
432
432
  min_proceeds_ustx: z
433
433
  .string()
434
434
  .describe("Minimum acceptable proceeds in micro-STX (slippage protection)"),
435
- },
435
+ }),
436
436
  }, async ({ market_id, shares, min_proceeds_ustx }) => {
437
437
  try {
438
438
  if (NETWORK !== "mainnet") {
@@ -478,7 +478,7 @@ Uses the sell-no-auto function with a minimum proceeds guard.
478
478
  The transaction will fail if proceeds fall below min_proceeds_ustx.
479
479
 
480
480
  Requires an unlocked wallet. Only available on mainnet.`,
481
- inputSchema: {
481
+ inputSchema: z.object({
482
482
  market_id: z
483
483
  .string()
484
484
  .describe("Market ID (epoch ms timestamp)"),
@@ -488,7 +488,7 @@ Requires an unlocked wallet. Only available on mainnet.`,
488
488
  min_proceeds_ustx: z
489
489
  .string()
490
490
  .describe("Minimum acceptable proceeds in micro-STX (slippage protection)"),
491
- },
491
+ }),
492
492
  }, async ({ market_id, shares, min_proceeds_ustx }) => {
493
493
  try {
494
494
  if (NETWORK !== "mainnet") {
@@ -533,11 +533,11 @@ Requires an unlocked wallet. Only available on mainnet.`,
533
533
  Call this after the market has been resolved to claim STX for the winning side shares you hold.
534
534
 
535
535
  Requires an unlocked wallet. Only available on mainnet.`,
536
- inputSchema: {
536
+ inputSchema: z.object({
537
537
  market_id: z
538
538
  .string()
539
539
  .describe("Market ID (epoch ms timestamp) of the resolved market"),
540
- },
540
+ }),
541
541
  }, async ({ market_id }) => {
542
542
  try {
543
543
  if (NETWORK !== "mainnet") {
@@ -577,7 +577,7 @@ If no address is provided, uses the currently configured wallet address.
577
577
  No wallet required when an address is explicitly supplied.
578
578
 
579
579
  Note: Stacks Market is only available on mainnet.`,
580
- inputSchema: {
580
+ inputSchema: z.object({
581
581
  market_id: z
582
582
  .string()
583
583
  .describe("Market ID (epoch ms timestamp)"),
@@ -585,7 +585,7 @@ Note: Stacks Market is only available on mainnet.`,
585
585
  .string()
586
586
  .optional()
587
587
  .describe("Stacks address to check position for. Uses configured wallet if not provided."),
588
- },
588
+ }),
589
589
  }, async ({ market_id, address }) => {
590
590
  try {
591
591
  if (NETWORK !== "mainnet") {
@@ -15,6 +15,6 @@
15
15
  * - styx_status: Deposit status by ID or txid
16
16
  * - styx_history: Deposit history for a Stacks address
17
17
  */
18
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18
+ import { McpServer } from "@modelcontextprotocol/server";
19
19
  export declare function registerStyxTools(server: McpServer): void;
20
20
  //# sourceMappingURL=styx.tools.d.ts.map