bsv-mcp 0.2.14 → 0.3.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 (83) hide show
  1. package/CHANGELOG.md +90 -10
  2. package/README.md +13 -13
  3. package/dist/app.html +483 -106
  4. package/dist/index.js +265787 -155292
  5. package/index.ts +505 -70
  6. package/package.json +24 -15
  7. package/prompts/bsvSdk/auth.ts +1 -1
  8. package/prompts/bsvSdk/cryptography.ts +1 -1
  9. package/prompts/bsvSdk/overview.ts +1 -1
  10. package/prompts/bsvSdk/primitives.ts +1 -1
  11. package/prompts/bsvSdk/script.ts +1 -1
  12. package/prompts/bsvSdk/transaction.ts +1 -1
  13. package/prompts/bsvSdk/wallet.ts +1 -1
  14. package/prompts/ordinals.ts +1 -1
  15. package/test-mcp-server.ts +26 -4
  16. package/tools/a2b/discover.ts +2 -2
  17. package/tools/bap/friend.ts +17 -24
  18. package/tools/bap/generate.test.ts +34 -32
  19. package/tools/bap/generate.ts +48 -43
  20. package/tools/bap/getCurrentAddress.ts +1 -3
  21. package/tools/bap/getId.test.ts +18 -10
  22. package/tools/bap/getId.ts +8 -6
  23. package/tools/bap/utils.ts +1 -1
  24. package/tools/bsocial/bmapReadPosts.ts +16 -2
  25. package/tools/bsocial/createPost.ts +21 -10
  26. package/tools/bsocial/readPosts.ts +10 -4
  27. package/tools/bsv/decodeTransaction.ts +1 -1
  28. package/tools/bsv/explore.ts +0 -4
  29. package/tools/bsv/getPrice.ts +1 -1
  30. package/tools/bsv/token.ts +14 -6
  31. package/tools/constants.ts +6 -2
  32. package/tools/index.ts +10 -10
  33. package/tools/mnee/getBalance.ts +1 -1
  34. package/tools/mnee/parseTx.ts +1 -1
  35. package/tools/mnee/sendMnee.ts +30 -8
  36. package/tools/ordinals/getInscription.ts +1 -3
  37. package/tools/ordinals/getTokenByIdOrTicker.ts +1 -3
  38. package/tools/ordinals/index.ts +4 -1
  39. package/tools/ordinals/marketListings.ts +1 -1
  40. package/tools/ordinals/marketSales.ts +1 -1
  41. package/tools/ordinals/searchInscriptions.ts +1 -1
  42. package/tools/utils/aip.ts +31 -22
  43. package/tools/utils/installAgentMaster.ts +8 -9
  44. package/tools/utils/toolRegistration.ts +21 -22
  45. package/tools/utils/transactionBuilder.ts +21 -14
  46. package/tools/wallet/a2bPublishMcp.ts +15 -5
  47. package/tools/wallet/brc100.ts +541 -310
  48. package/tools/wallet/cancelListing.ts +29 -37
  49. package/tools/wallet/createOrdinals.ts +43 -27
  50. package/tools/wallet/fetchPaymentUtxos.ts +7 -5
  51. package/tools/wallet/gatherCollectionInfo.ts +3 -5
  52. package/tools/wallet/getAddress.ts +1 -4
  53. package/tools/wallet/getBalance.ts +15 -15
  54. package/tools/wallet/{getBalanceDroplet.ts → getBalanceDroplit.ts} +6 -6
  55. package/tools/wallet/getBsv21Balances.ts +20 -14
  56. package/tools/wallet/getLockData.ts +11 -11
  57. package/tools/wallet/getOrdinals.ts +26 -20
  58. package/tools/wallet/integratedWallet.ts +55 -53
  59. package/tools/wallet/listOrdinal.ts +40 -39
  60. package/tools/wallet/listTokens.ts +18 -17
  61. package/tools/wallet/lockBsv.ts +36 -22
  62. package/tools/wallet/mintCollection.ts +15 -10
  63. package/tools/wallet/opnsDeregister.ts +29 -39
  64. package/tools/wallet/opnsRegister.ts +29 -39
  65. package/tools/wallet/purchaseListing.test.ts +27 -0
  66. package/tools/wallet/purchaseListing.ts +59 -40
  67. package/tools/wallet/refreshUtxos.ts +1 -1
  68. package/tools/wallet/schemas.ts +0 -13
  69. package/tools/wallet/sendAllBsv.ts +26 -20
  70. package/tools/wallet/sendBsv.ts +107 -0
  71. package/tools/wallet/setupDroplit.ts +157 -0
  72. package/tools/wallet/signBsm.ts +18 -18
  73. package/tools/wallet/sweepBsv.ts +48 -27
  74. package/tools/wallet/sweepBsv21.ts +42 -28
  75. package/tools/wallet/sweepOrdinals.ts +38 -26
  76. package/tools/wallet/tools.ts +11 -13
  77. package/tools/wallet/transactionHelper.ts +13 -5
  78. package/tools/wallet/transferOrdToken.ts +72 -84
  79. package/tools/wallet/unlockBsv.ts +23 -17
  80. package/tools/wallet/utxo.ts +1 -0
  81. package/tools/wallet/wallet.ts +1 -13
  82. package/tools/wallet/sendToAddress.ts +0 -74
  83. package/tools/wallet/setupDroplet.ts +0 -161
@@ -1,5 +1,9 @@
1
- import { P2PKH, type PrivateKey, Script } from "@bsv/sdk";
2
- import { DropletClient, type DropletConfig } from "../../utils/droplet";
1
+ import { type PrivateKey, Utils } from "@bsv/sdk";
2
+ import { DroplitClient, type DroplitConfig } from "../../utils/droplit";
3
+ import {
4
+ buildAndSendTransaction,
5
+ buildOpReturnScript,
6
+ } from "../utils/transactionBuilder";
3
7
  import { Wallet } from "./wallet";
4
8
 
5
9
  export interface IntegratedWalletConfig {
@@ -7,26 +11,26 @@ export interface IntegratedWalletConfig {
7
11
  paymentKey?: PrivateKey;
8
12
  identityKey?: PrivateKey;
9
13
 
10
- // Droplet API config
11
- useDropletApi?: boolean;
12
- dropletConfig?: DropletConfig;
14
+ // Droplit API config
15
+ useDroplitApi?: boolean;
16
+ droplitConfig?: DroplitConfig;
13
17
  }
14
18
 
15
19
  /**
16
- * Integrated wallet that can use either local keys or Droplet API
20
+ * Integrated wallet that can use either local keys or Droplit API
17
21
  */
18
22
  export class IntegratedWallet {
19
23
  private localWallet?: Wallet;
20
- private dropletClient?: DropletClient;
24
+ private droplitClient?: DroplitClient;
21
25
 
22
- constructor(private config: IntegratedWalletConfig) {
23
- if (config.useDropletApi && config.dropletConfig) {
24
- // If we have a payment key, use it for auth with Droplet API
26
+ constructor(config: IntegratedWalletConfig) {
27
+ if (config.useDroplitApi && config.droplitConfig) {
28
+ // If we have a payment key, use it for auth with Droplit API
25
29
  if (config.paymentKey) {
26
- config.dropletConfig.authKey = config.paymentKey;
30
+ config.droplitConfig.authKey = config.paymentKey;
27
31
  }
28
- this.dropletClient = new DropletClient(config.dropletConfig);
29
- console.error("IntegratedWallet: Using Droplet API mode");
32
+ this.droplitClient = new DroplitClient(config.droplitConfig);
33
+ console.error("IntegratedWallet: Using Droplit API mode");
30
34
  } else if (config.paymentKey) {
31
35
  this.localWallet = new Wallet(config.paymentKey, config.identityKey);
32
36
  console.error("IntegratedWallet: Using local wallet mode");
@@ -37,21 +41,22 @@ export class IntegratedWallet {
37
41
  }
38
42
  }
39
43
 
40
- get isDropletMode(): boolean {
41
- return !!this.dropletClient;
44
+ get isDroplitMode(): boolean {
45
+ return !!this.droplitClient;
42
46
  }
43
47
 
44
48
  get hasWallet(): boolean {
45
- return !!this.localWallet || !!this.dropletClient;
49
+ return !!this.localWallet || !!this.droplitClient;
46
50
  }
47
51
 
48
52
  async getBalance(): Promise<number> {
49
- if (this.dropletClient) {
50
- const status = await this.dropletClient.getFaucetStatus();
53
+ if (this.droplitClient) {
54
+ const status = await this.droplitClient.getFaucetStatus();
51
55
  return status.balance_satoshis;
52
56
  }
53
57
  if (this.localWallet) {
54
- return this.localWallet.getTotalBalance();
58
+ const { paymentUtxos } = await this.localWallet.getUtxos();
59
+ return paymentUtxos.reduce((sum, utxo) => sum + utxo.satoshis, 0);
55
60
  }
56
61
  throw new Error("No wallet configured");
57
62
  }
@@ -59,52 +64,49 @@ export class IntegratedWallet {
59
64
  async sendToAddress(
60
65
  address: string,
61
66
  satoshis: number,
62
- description?: string,
63
67
  ): Promise<{ txid: string }> {
64
- if (this.dropletClient) {
65
- // For Droplet API, we use the tap endpoint which sends the faucet's fixed amount
66
- // Note: Droplet API doesn't support custom amounts, it uses fixed_drop_sats
67
- const response = await this.dropletClient.tap(address);
68
+ if (this.droplitClient) {
69
+ // For Droplit API, we use the tap endpoint which sends the faucet's fixed amount
70
+ // Note: Droplit API doesn't support custom amounts, it uses fixed_drop_sats
71
+ const response = await this.droplitClient.tap(address);
68
72
  return { txid: response.txid };
69
73
  }
70
74
  if (this.localWallet) {
71
- const tx = await this.localWallet.createAction({
72
- description: description || "Send to address",
73
- outputs: [
74
- {
75
- lockingScript: new P2PKH().lock(address).toHex(),
76
- satoshis,
77
- outputDescription: `Payment to ${address}`,
78
- },
79
- ],
80
- });
81
- return { txid: tx.txid };
75
+ return await this.localWallet.sendToAddress(address, satoshis);
82
76
  }
83
77
  throw new Error("No wallet configured");
84
78
  }
85
79
 
86
80
  async pushData(data: string[], encoding = "hex"): Promise<{ txid: string }> {
87
- if (this.dropletClient) {
88
- const response = await this.dropletClient.push(data, encoding);
81
+ if (this.droplitClient) {
82
+ const response = await this.droplitClient.push(data, encoding);
89
83
  return { txid: response.txid };
90
84
  }
91
85
  if (this.localWallet) {
92
- // For local wallet, we need to implement OP_RETURN transaction
93
- // This is a simplified version - you might want to enhance this
94
- const opReturnScript = Script.fromASM(
95
- `OP_FALSE OP_RETURN ${data.join(" ")}`,
86
+ const paymentKey = this.localWallet.getPaymentKey();
87
+ const changeAddress = this.localWallet.getAddress();
88
+ if (!paymentKey || !changeAddress) {
89
+ throw new Error("Local wallet has no payment key");
90
+ }
91
+ if (encoding !== "hex" && encoding !== "utf8" && encoding !== "base64") {
92
+ throw new Error(
93
+ `Unsupported push data encoding "${encoding}"; expected hex, utf8 or base64`,
94
+ );
95
+ }
96
+ const script = buildOpReturnScript(
97
+ data.map((item) => Utils.toArray(item, encoding)),
96
98
  );
97
- const tx = await this.localWallet.createAction({
98
- description: "Push data",
99
- outputs: [
100
- {
101
- lockingScript: opReturnScript.toHex(),
102
- satoshis: 0,
103
- outputDescription: "OP_RETURN data",
104
- },
105
- ],
99
+ const { paymentUtxos } = await this.localWallet.getUtxos();
100
+ const result = await buildAndSendTransaction({
101
+ outputs: [{ script, satoshis: 0 }],
102
+ utxos: paymentUtxos,
103
+ changeAddress,
104
+ paymentKey,
106
105
  });
107
- return { txid: tx.txid };
106
+ if (!result.success || !result.txid) {
107
+ throw new Error(result.error ?? "Failed to broadcast OP_RETURN data");
108
+ }
109
+ return { txid: result.txid };
108
110
  }
109
111
  throw new Error("No wallet configured");
110
112
  }
@@ -114,7 +116,7 @@ export class IntegratedWallet {
114
116
  return this.localWallet;
115
117
  }
116
118
 
117
- getDropletClient(): DropletClient | undefined {
118
- return this.dropletClient;
119
+ getDroplitClient(): DroplitClient | undefined {
120
+ return this.droplitClient;
119
121
  }
120
122
  }
@@ -1,86 +1,87 @@
1
- import type { OneSatContext } from '@1sat/actions'
2
- import { listOrdinal } from '@1sat/actions'
3
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
4
- import { z } from 'zod'
5
-
6
- const walletOutputSchema = z.object({
7
- outpoint: z.string(),
8
- satoshis: z.number().optional(),
9
- tags: z.array(z.string()).optional(),
10
- customInstructions: z.string().optional(),
11
- lockingScript: z.string().optional(),
12
- })
1
+ import type { OneSatContext } from "@1sat/actions";
2
+ import { sellOrdinal } from "@1sat/actions";
3
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { z } from "zod";
5
+ import { assertBroadcastAllowed } from "../../utils/broadcastGuard";
13
6
 
14
7
  const listOrdinalArgsSchema = z.object({
15
- ordinal: walletOutputSchema.describe(
16
- 'WalletOutput of the ordinal to list (from wallet_getOrdinals)',
17
- ),
18
- inputBEEF: z
19
- .array(z.number())
8
+ id: z
9
+ .string()
20
10
  .describe(
21
- "BEEF bytes from listOutputs (include: 'entire transactions')",
11
+ "Tracking id of the ordinal in the ordinals basket (the 'id:' tag from wallet_getOrdinals)",
22
12
  ),
23
- price: z.number().describe('Price in satoshis'),
13
+ price: z
14
+ .number()
15
+ .int()
16
+ .positive()
17
+ .describe("Price in satoshis; whole satoshis above zero"),
24
18
  payAddress: z
25
19
  .string()
26
- .describe('Address to receive payment on purchase'),
27
- })
20
+ .optional()
21
+ .describe(
22
+ "Address to receive payment on purchase. Defaults to the wallet's P1SAT '1sat 0' address.",
23
+ ),
24
+ });
28
25
 
29
26
  export function registerListOrdinalTool(
30
27
  server: McpServer,
31
28
  ctx: OneSatContext | undefined,
32
29
  ) {
33
30
  server.tool(
34
- 'wallet_listOrdinal',
35
- 'List an ordinal for sale on the marketplace',
31
+ "wallet_listOrdinal",
32
+ "List an ordinal for sale on the marketplace",
36
33
  { ...listOrdinalArgsSchema.shape },
37
- async ({ ordinal, inputBEEF, price, payAddress }) => {
34
+ async ({ id, price, payAddress }) => {
38
35
  if (!ctx) {
39
36
  return {
40
37
  content: [
41
38
  {
42
- type: 'text',
43
- text: 'Wallet not initialized. Please configure a wallet before listing.',
39
+ type: "text",
40
+ text: "Wallet not initialized. Please configure a wallet before listing.",
44
41
  },
45
42
  ],
46
43
  isError: true,
47
- }
44
+ };
48
45
  }
49
46
 
50
47
  try {
51
- const result = await listOrdinal.execute(ctx, {
52
- ordinal: ordinal as Parameters<typeof listOrdinal.execute>[1]['ordinal'],
53
- inputBEEF,
48
+ assertBroadcastAllowed("wallet_listOrdinal");
49
+ const result = await sellOrdinal.execute(ctx, {
50
+ id,
54
51
  price,
55
52
  payAddress,
56
- })
53
+ });
57
54
 
58
55
  if (result.error) {
59
56
  return {
60
- content: [{ type: 'text', text: result.error }],
57
+ content: [{ type: "text", text: result.error }],
61
58
  isError: true,
62
- }
59
+ };
63
60
  }
64
61
 
65
62
  return {
66
63
  content: [
67
64
  {
68
- type: 'text',
69
- text: JSON.stringify(result, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2),
65
+ type: "text",
66
+ text: JSON.stringify(
67
+ result,
68
+ (_, v) => (typeof v === "bigint" ? v.toString() : v),
69
+ 2,
70
+ ),
70
71
  },
71
72
  ],
72
- }
73
+ };
73
74
  } catch (err: unknown) {
74
75
  return {
75
76
  content: [
76
77
  {
77
- type: 'text',
78
+ type: "text",
78
79
  text: err instanceof Error ? err.message : String(err),
79
80
  },
80
81
  ],
81
82
  isError: true,
82
- }
83
+ };
83
84
  }
84
85
  },
85
- )
86
+ );
86
87
  }
@@ -1,28 +1,29 @@
1
- import { listTokens, type OneSatContext } from '@1sat/actions'
2
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
3
- import { z } from 'zod'
1
+ import { listBsv21, type OneSatContext } from "@1sat/actions";
2
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { z } from "zod";
4
4
 
5
- export function registerListTokensTool(
6
- server: McpServer,
7
- ctx?: OneSatContext,
8
- ) {
5
+ export function registerListTokensTool(server: McpServer, ctx?: OneSatContext) {
9
6
  server.tool(
10
- 'wallet_listTokens',
11
- 'List BSV21 token outputs in the wallet',
7
+ "wallet_listTokens",
8
+ "List BSV21 token outputs in the wallet",
12
9
  {
13
- limit: z.number().int().optional().describe('Max number of results to return'),
10
+ limit: z
11
+ .number()
12
+ .int()
13
+ .optional()
14
+ .describe("Max number of results to return"),
14
15
  },
15
16
  async (params) => {
16
17
  try {
17
- if (!ctx) throw new Error('BRC-100 wallet context not available')
18
- const result = await listTokens.execute(ctx, params)
18
+ if (!ctx) throw new Error("BRC-100 wallet context not available");
19
+ const result = await listBsv21.execute(ctx, params);
19
20
  return {
20
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
21
- }
21
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
22
+ };
22
23
  } catch (err: unknown) {
23
- const msg = err instanceof Error ? err.message : String(err)
24
- return { content: [{ type: 'text', text: msg }], isError: true }
24
+ const msg = err instanceof Error ? err.message : String(err);
25
+ return { content: [{ type: "text", text: msg }], isError: true };
25
26
  }
26
27
  },
27
- )
28
+ );
28
29
  }
@@ -1,69 +1,83 @@
1
- import type { OneSatContext } from '@1sat/actions'
2
- import { lockBsv } from '@1sat/actions'
3
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
4
- import { z } from 'zod'
1
+ import type { OneSatContext } from "@1sat/actions";
2
+ import { lockBsv } from "@1sat/actions";
3
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { z } from "zod";
5
+ import { assertBroadcastAllowed } from "../../utils/broadcastGuard";
5
6
 
6
7
  const lockBsvArgsSchema = z.object({
7
8
  requests: z
8
9
  .array(
9
10
  z.object({
10
- satoshis: z.number().describe('Amount in satoshis to lock'),
11
- until: z.number().describe('Block height until which to lock'),
11
+ satoshis: z
12
+ .number()
13
+ .int()
14
+ .positive()
15
+ .describe("Amount in satoshis to lock; whole satoshis above zero"),
16
+ until: z
17
+ .number()
18
+ .int()
19
+ .positive()
20
+ .describe("Block height until which to lock"),
12
21
  }),
13
22
  )
14
- .describe('Array of lock requests'),
15
- })
23
+ .describe("Array of lock requests"),
24
+ });
16
25
 
17
26
  export function registerLockBsvTool(
18
27
  server: McpServer,
19
28
  ctx: OneSatContext | undefined,
20
29
  ) {
21
30
  server.tool(
22
- 'wallet_lockBsv',
23
- 'Lock BSV until a specific block height',
31
+ "wallet_lockBsv",
32
+ "Lock BSV until a specific block height",
24
33
  { ...lockBsvArgsSchema.shape },
25
34
  async ({ requests }) => {
26
35
  if (!ctx) {
27
36
  return {
28
37
  content: [
29
38
  {
30
- type: 'text',
31
- text: 'Wallet not initialized. Please configure a wallet before locking.',
39
+ type: "text",
40
+ text: "Wallet not initialized. Please configure a wallet before locking.",
32
41
  },
33
42
  ],
34
43
  isError: true,
35
- }
44
+ };
36
45
  }
37
46
 
38
47
  try {
39
- const result = await lockBsv.execute(ctx, { requests })
48
+ assertBroadcastAllowed("wallet_lockBsv");
49
+ const result = await lockBsv.execute(ctx, { requests });
40
50
 
41
51
  if (result.error) {
42
52
  return {
43
- content: [{ type: 'text', text: result.error }],
53
+ content: [{ type: "text", text: result.error }],
44
54
  isError: true,
45
- }
55
+ };
46
56
  }
47
57
 
48
58
  return {
49
59
  content: [
50
60
  {
51
- type: 'text',
52
- text: JSON.stringify(result, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2),
61
+ type: "text",
62
+ text: JSON.stringify(
63
+ result,
64
+ (_, v) => (typeof v === "bigint" ? v.toString() : v),
65
+ 2,
66
+ ),
53
67
  },
54
68
  ],
55
- }
69
+ };
56
70
  } catch (err: unknown) {
57
71
  return {
58
72
  content: [
59
73
  {
60
- type: 'text',
74
+ type: "text",
61
75
  text: err instanceof Error ? err.message : String(err),
62
76
  },
63
77
  ],
64
78
  isError: true,
65
- }
79
+ };
66
80
  }
67
81
  },
68
- )
82
+ );
69
83
  }
@@ -12,11 +12,11 @@ import type {
12
12
  CreateOrdinalsCollectionMetadata,
13
13
  CreateOrdinalsConfig,
14
14
  LocalSigner,
15
- RarityLabels,
16
15
  } from "js-1sat-ord";
17
16
  import { createOrdinals } from "js-1sat-ord";
18
17
  import { z } from "zod";
19
18
  import { V5Broadcaster } from "../../utils/broadcaster";
19
+ import { assertBroadcastAllowed } from "../../utils/broadcastGuard";
20
20
  import type { Wallet } from "./wallet";
21
21
 
22
22
  const mintCollectionArgsSchema = z.object({
@@ -46,8 +46,6 @@ const mintCollectionArgsSchema = z.object({
46
46
  .describe("Skip broadcasting transactions (for testing)"),
47
47
  });
48
48
 
49
- type MintCollectionArgs = z.infer<typeof mintCollectionArgsSchema>;
50
-
51
49
  interface ImageFile {
52
50
  path: string;
53
51
  name: string;
@@ -121,10 +119,20 @@ export function registerMintCollectionTool(server: McpServer, wallet: Wallet) {
121
119
  "wallet_mintCollection",
122
120
  "Mint a collection of ordinals from a folder of images with proper metadata. This tool creates a collection inscription first, then mints each image as a collection item with the appropriate metadata linking it to the collection.",
123
121
  { ...mintCollectionArgsSchema.shape },
124
- async ({ folderPath, collectionName, description, rarityLabels, traits: traitsJson, skipBroadcast }): Promise<CallToolResult> => {
122
+ async ({
123
+ folderPath,
124
+ collectionName,
125
+ description,
126
+ rarityLabels,
127
+ traits: traitsJson,
128
+ skipBroadcast,
129
+ }): Promise<CallToolResult> => {
125
130
  try {
131
+ assertBroadcastAllowed("wallet_mintCollection");
126
132
  // Parse traits JSON string if provided
127
- const traits: Record<string, string[]> | undefined = traitsJson ? JSON.parse(traitsJson) : undefined;
133
+ const traits: Record<string, string[]> | undefined = traitsJson
134
+ ? JSON.parse(traitsJson)
135
+ : undefined;
128
136
  // Get keys from wallet
129
137
  const paymentPk = wallet.getPaymentKey();
130
138
  if (!paymentPk) {
@@ -145,8 +153,7 @@ export function registerMintCollectionTool(server: McpServer, wallet: Wallet) {
145
153
  description: description,
146
154
  quantity: imageFiles.length,
147
155
  // Transform rarity labels to correct format
148
- rarityLabels:
149
- rarityLabels?.map((r) => ({ label: r.label })) || [],
156
+ rarityLabels: rarityLabels?.map((r) => ({ label: r.label })) || [],
150
157
  // Transform traits - need to match the CollectionTraits type
151
158
  traits: traits
152
159
  ? Object.entries(traits).reduce((acc, [key, values]) => {
@@ -262,9 +269,7 @@ export function registerMintCollectionTool(server: McpServer, wallet: Wallet) {
262
269
  (i / imageFiles.length) * rarityLabels.length,
263
270
  );
264
271
  const rarityItem =
265
- rarityLabels[
266
- Math.min(rarityIndex, rarityLabels.length - 1)
267
- ];
272
+ rarityLabels[Math.min(rarityIndex, rarityLabels.length - 1)];
268
273
  if (rarityItem) {
269
274
  itemSubTypeData.rarityLabel = [
270
275
  { [rarityItem.label]: `${rarityItem.percentage}%` },
@@ -1,80 +1,70 @@
1
- import type { OneSatContext } from '@1sat/actions'
2
- import { opnsDeregister } from '@1sat/actions'
3
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
4
- import { z } from 'zod'
5
-
6
- const walletOutputSchema = z.object({
7
- outpoint: z.string(),
8
- satoshis: z.number().optional(),
9
- tags: z.array(z.string()).optional(),
10
- customInstructions: z.string().optional(),
11
- lockingScript: z.string().optional(),
12
- })
1
+ import type { OneSatContext } from "@1sat/actions";
2
+ import { deregisterOpns } from "@1sat/actions";
3
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { z } from "zod";
5
+ import { assertBroadcastAllowed } from "../../utils/broadcastGuard";
13
6
 
14
7
  const opnsDeregisterArgsSchema = z.object({
15
- ordinal: walletOutputSchema.describe(
16
- 'WalletOutput of the OpNS ordinal to deregister (from listOutputs)',
17
- ),
18
- inputBEEF: z
19
- .array(z.number())
20
- .describe(
21
- "BEEF bytes from listOutputs (include: 'entire transactions')",
22
- ),
23
- })
8
+ id: z
9
+ .string()
10
+ .describe("Tracking id of the OpNS name in the wallet's OPNS basket"),
11
+ });
24
12
 
25
13
  export function registerOpnsDeregisterTool(
26
14
  server: McpServer,
27
15
  ctx: OneSatContext | undefined,
28
16
  ) {
29
17
  server.tool(
30
- 'wallet_opnsDeregister',
31
- 'Deregister an OpNS name',
18
+ "wallet_opnsDeregister",
19
+ "Deregister an OpNS name",
32
20
  { ...opnsDeregisterArgsSchema.shape },
33
- async ({ ordinal, inputBEEF }) => {
21
+ async ({ id }) => {
34
22
  if (!ctx) {
35
23
  return {
36
24
  content: [
37
25
  {
38
- type: 'text',
39
- text: 'Wallet not initialized. Please configure a wallet before deregistering.',
26
+ type: "text",
27
+ text: "Wallet not initialized. Please configure a wallet before deregistering.",
40
28
  },
41
29
  ],
42
30
  isError: true,
43
- }
31
+ };
44
32
  }
45
33
 
46
34
  try {
47
- const result = await opnsDeregister.execute(ctx, {
48
- ordinal: ordinal as Parameters<typeof opnsDeregister.execute>[1]['ordinal'],
49
- inputBEEF,
50
- })
35
+ assertBroadcastAllowed("wallet_opnsDeregister");
36
+ const result = await deregisterOpns.execute(ctx, { id });
51
37
 
52
38
  if (result.error) {
53
39
  return {
54
- content: [{ type: 'text', text: result.error }],
40
+ content: [{ type: "text", text: result.error }],
55
41
  isError: true,
56
- }
42
+ };
57
43
  }
58
44
 
59
45
  return {
60
46
  content: [
61
47
  {
62
- type: 'text',
63
- text: JSON.stringify(result, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2),
48
+ type: "text",
49
+ text: JSON.stringify(
50
+ result,
51
+ (_, v) => (typeof v === "bigint" ? v.toString() : v),
52
+ 2,
53
+ ),
64
54
  },
65
55
  ],
66
- }
56
+ };
67
57
  } catch (err: unknown) {
68
58
  return {
69
59
  content: [
70
60
  {
71
- type: 'text',
61
+ type: "text",
72
62
  text: err instanceof Error ? err.message : String(err),
73
63
  },
74
64
  ],
75
65
  isError: true,
76
- }
66
+ };
77
67
  }
78
68
  },
79
- )
69
+ );
80
70
  }