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
@@ -0,0 +1,157 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import {
4
+ createErrorResponse,
5
+ createSuccessResponse,
6
+ } from "../utils/errorHandler";
7
+ import type { IntegratedWallet } from "./integratedWallet";
8
+
9
+ /**
10
+ * Droplit setup and faucet administration.
11
+ *
12
+ * These were one tool behind an `action` enum, which made every argument
13
+ * conditional on the action and left the caller to work out which combination
14
+ * was valid. They are three tools now, each declaring only what it needs.
15
+ */
16
+
17
+ type DroplitClient = NonNullable<
18
+ ReturnType<IntegratedWallet["getDroplitClient"]>
19
+ >;
20
+
21
+ /**
22
+ * Every Droplit call needs a configured client and every write needs the
23
+ * signing key, so resolve both up front rather than failing part-way through a
24
+ * request with a different message depending on which tool was called.
25
+ */
26
+ function requireDroplit(integratedWallet: IntegratedWallet): {
27
+ apiUrl: string;
28
+ publicKeyHex: string;
29
+ client: DroplitClient;
30
+ } {
31
+ const client = integratedWallet.getDroplitClient();
32
+ if (!client) {
33
+ throw new Error(
34
+ "Droplit client is not configured. Set DROPLIT_API_URL and DROPLIT_FAUCET_NAME.",
35
+ );
36
+ }
37
+
38
+ const { apiUrl, authKey } = client.getConfig();
39
+ if (!authKey) {
40
+ throw new Error(
41
+ "Droplit requests must be signed, but no auth key is configured.",
42
+ );
43
+ }
44
+
45
+ return { apiUrl, publicKeyHex: authKey.toPublicKey().toString(), client };
46
+ }
47
+
48
+ async function failIfNotOk(
49
+ response: Response,
50
+ attempted: string,
51
+ ): Promise<void> {
52
+ if (response.ok) return;
53
+ throw new Error(
54
+ `${attempted} failed (${response.status}): ${await response.text()}`,
55
+ );
56
+ }
57
+
58
+ export function registerSetupDroplitTools(
59
+ server: McpServer,
60
+ integratedWallet: IntegratedWallet,
61
+ ) {
62
+ server.tool(
63
+ "wallet_registerDroplitKey",
64
+ "Registers this wallet's public key with the Droplit API so it can sign authenticated faucet requests. Run once before creating or operating a faucet.",
65
+ {},
66
+ async () => {
67
+ try {
68
+ const { apiUrl, publicKeyHex } = requireDroplit(integratedWallet);
69
+
70
+ const response = await fetch(`${apiUrl}/auth/register`, {
71
+ method: "POST",
72
+ headers: { "Content-Type": "application/json" },
73
+ body: JSON.stringify({ publicKey: publicKeyHex }),
74
+ });
75
+ await failIfNotOk(response, "Registering the Droplit key");
76
+
77
+ return createSuccessResponse({
78
+ message: "Public key registered with Droplit",
79
+ publicKey: publicKeyHex,
80
+ });
81
+ } catch (error) {
82
+ return createErrorResponse(error);
83
+ }
84
+ },
85
+ );
86
+
87
+ server.tool(
88
+ "wallet_createDroplitFaucet",
89
+ "Provisions a new Droplit faucet owned by this wallet. Register the wallet's public key first with wallet_registerDroplitKey.",
90
+ {
91
+ faucetName: z
92
+ .string()
93
+ .min(1)
94
+ .describe("Identifier for the new faucet; must be unique"),
95
+ fixedDropSats: z
96
+ .number()
97
+ .int()
98
+ .positive()
99
+ .optional()
100
+ .describe(
101
+ "Satoshis paid out per tap. Server default applies if unset.",
102
+ ),
103
+ maxConsolidationInputs: z
104
+ .number()
105
+ .int()
106
+ .positive()
107
+ .optional()
108
+ .describe("Cap on inputs the faucet consolidates in one transaction"),
109
+ },
110
+ async ({ faucetName, fixedDropSats, maxConsolidationInputs }) => {
111
+ try {
112
+ const { client } = requireDroplit(integratedWallet);
113
+
114
+ // Ownership comes from the BRC-103/104 identity established by the
115
+ // handshake, not from the body, so the key is never sent here.
116
+ const body = {
117
+ name: faucetName,
118
+ ...(fixedDropSats !== undefined && {
119
+ fixed_drop_sats: fixedDropSats,
120
+ }),
121
+ ...(maxConsolidationInputs !== undefined && {
122
+ max_consolidation_inputs: maxConsolidationInputs,
123
+ }),
124
+ };
125
+
126
+ const response = await client.authenticatedFetch("/faucets", {
127
+ method: "POST",
128
+ body,
129
+ });
130
+ await failIfNotOk(response, "Creating the Droplit faucet");
131
+
132
+ return createSuccessResponse({
133
+ message: "Faucet created",
134
+ faucet: await response.json(),
135
+ });
136
+ } catch (error) {
137
+ return createErrorResponse(error);
138
+ }
139
+ },
140
+ );
141
+
142
+ server.tool(
143
+ "wallet_checkDroplitFaucetStatus",
144
+ "Reads the status of the configured Droplit faucet, including balance and payout settings.",
145
+ {},
146
+ async () => {
147
+ try {
148
+ const { client } = requireDroplit(integratedWallet);
149
+ return createSuccessResponse({
150
+ faucetStatus: await client.getFaucetStatus(),
151
+ });
152
+ } catch (error) {
153
+ return createErrorResponse(error);
154
+ }
155
+ },
156
+ );
157
+ }
@@ -1,29 +1,29 @@
1
- import { signBsm, type OneSatContext } from '@1sat/actions'
2
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
3
- import { z } from 'zod'
1
+ import { type OneSatContext, signBsm } from "@1sat/actions";
2
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { z } from "zod";
4
4
 
5
- export function registerSignBsmTool(
6
- server: McpServer,
7
- ctx?: OneSatContext,
8
- ) {
5
+ export function registerSignBsmTool(server: McpServer, ctx?: OneSatContext) {
9
6
  server.tool(
10
- 'wallet_signBsm',
11
- 'Sign a message using BSM (Bitcoin Signed Message) format',
7
+ "wallet_signBsm",
8
+ "Sign a message using BSM (Bitcoin Signed Message) format",
12
9
  {
13
- message: z.string().describe('The message to sign'),
14
- encoding: z.enum(['utf8', 'hex', 'base64']).optional().describe('Message encoding format'),
10
+ message: z.string().describe("The message to sign"),
11
+ encoding: z
12
+ .enum(["utf8", "hex", "base64"])
13
+ .optional()
14
+ .describe("Message encoding format"),
15
15
  },
16
16
  async (params) => {
17
17
  try {
18
- if (!ctx) throw new Error('BRC-100 wallet context not available')
19
- const result = await signBsm.execute(ctx, params)
18
+ if (!ctx) throw new Error("BRC-100 wallet context not available");
19
+ const result = await signBsm.execute(ctx, params);
20
20
  return {
21
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
22
- }
21
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
22
+ };
23
23
  } catch (err: unknown) {
24
- const msg = err instanceof Error ? err.message : String(err)
25
- 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 };
26
26
  }
27
27
  },
28
- )
28
+ );
29
29
  }
@@ -1,70 +1,91 @@
1
- import type { OneSatContext } from '@1sat/actions'
2
- import { sweepBsv } 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 { sweepBsv } from "@1sat/actions";
3
+ import { PrivateKey } from "@bsv/sdk";
4
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { z } from "zod";
6
+ import { assertBroadcastAllowed } from "../../utils/broadcastGuard";
7
+ import { redactKeyMaterial, redactUnknown } from "../../utils/redact";
5
8
 
6
9
  const sweepInputSchema = z.object({
7
- outpoint: z.string().describe('Outpoint (txid_vout)'),
8
- satoshis: z.number().int().describe('Satoshis in output'),
9
- lockingScript: z.string().describe('Locking script hex'),
10
- })
10
+ outpoint: z.string().describe("Outpoint (txid_vout)"),
11
+ satoshis: z.number().int().describe("Satoshis in output"),
12
+ lockingScript: z.string().describe("Locking script hex"),
13
+ });
11
14
 
12
15
  const sweepBsvSchema = z.object({
13
- inputs: z.array(sweepInputSchema).describe('UTXOs to sweep (use prepareSweepInputs to build these)'),
14
- wif: z.string().describe('WIF private key controlling the inputs'),
15
- amount: z.number().int().optional().describe('Amount to sweep (satoshis). If omitted, sweeps all input value.'),
16
- })
16
+ inputs: z
17
+ .array(sweepInputSchema)
18
+ .describe("UTXOs to sweep (use prepareSweepInputs to build these)"),
19
+ wif: z.string().describe("WIF private key controlling the inputs"),
20
+ amount: z
21
+ .number()
22
+ .int()
23
+ .optional()
24
+ .describe(
25
+ "Amount to sweep (satoshis). If omitted, sweeps all input value.",
26
+ ),
27
+ });
17
28
 
18
29
  export function registerSweepBsvTool(
19
30
  server: McpServer,
20
31
  ctx: OneSatContext | undefined,
21
32
  ) {
22
33
  server.tool(
23
- 'wallet_sweepBsv',
24
- 'Sweep BSV from an external WIF private key into the wallet',
34
+ "wallet_sweepBsv",
35
+ "Sweep BSV from an external WIF private key into the wallet",
25
36
  { ...sweepBsvSchema.shape },
26
37
  async ({ inputs, wif, amount }) => {
27
38
  if (!ctx) {
28
39
  return {
29
40
  content: [
30
41
  {
31
- type: 'text' as const,
32
- text: 'BRC-100 wallet context not available',
42
+ type: "text" as const,
43
+ text: "BRC-100 wallet context not available",
33
44
  },
34
45
  ],
35
46
  isError: true,
36
- }
47
+ };
37
48
  }
38
49
 
39
50
  try {
40
- const result = await sweepBsv.execute(ctx, { inputs, wif, amount })
51
+ assertBroadcastAllowed("wallet_sweepBsv");
52
+ const key = PrivateKey.fromWif(wif);
53
+ const result = await sweepBsv.execute(ctx, {
54
+ inputs,
55
+ keys: inputs.map(() => key),
56
+ amount,
57
+ });
41
58
 
42
59
  if (result.error) {
43
60
  return {
44
- content: [{ type: 'text' as const, text: result.error }],
61
+ content: [
62
+ { type: "text" as const, text: redactKeyMaterial(result.error) },
63
+ ],
45
64
  isError: true,
46
- }
65
+ };
47
66
  }
48
67
 
49
68
  return {
50
69
  content: [
51
70
  {
52
- type: 'text' as const,
53
- text: JSON.stringify(result, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2),
71
+ type: "text" as const,
72
+ text: redactUnknown(result, 2),
54
73
  },
55
74
  ],
56
- }
75
+ };
57
76
  } catch (error) {
58
77
  return {
59
78
  content: [
60
79
  {
61
- type: 'text' as const,
62
- text: error instanceof Error ? error.message : String(error),
80
+ type: "text" as const,
81
+ text: redactKeyMaterial(
82
+ error instanceof Error ? error.message : String(error),
83
+ ),
63
84
  },
64
85
  ],
65
86
  isError: true,
66
- }
87
+ };
67
88
  }
68
89
  },
69
- )
90
+ );
70
91
  }
@@ -1,71 +1,85 @@
1
- import type { OneSatContext } from '@1sat/actions'
2
- import { sweepBsv21 } 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 { sweepBsv21 } from "@1sat/actions";
3
+ import { PrivateKey } from "@bsv/sdk";
4
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { z } from "zod";
6
+ import { assertBroadcastAllowed } from "../../utils/broadcastGuard";
7
+ import { redactKeyMaterial, redactUnknown } from "../../utils/redact";
5
8
 
6
9
  const sweepBsv21InputSchema = z.object({
7
- outpoint: z.string().describe('Outpoint (txid_vout)'),
8
- satoshis: z.number().int().describe('Satoshis (should be 1)'),
9
- lockingScript: z.string().describe('Locking script hex'),
10
- tokenId: z.string().describe('Token ID (txid_vout format)'),
11
- amount: z.string().describe('Token amount as string'),
12
- })
10
+ outpoint: z.string().describe("Outpoint (txid_vout)"),
11
+ satoshis: z.number().int().describe("Satoshis (should be 1)"),
12
+ lockingScript: z.string().describe("Locking script hex"),
13
+ tokenId: z.string().describe("Token ID (txid_vout format)"),
14
+ amount: z.string().describe("Token amount as string"),
15
+ });
13
16
 
14
17
  const sweepBsv21Schema = z.object({
15
- inputs: z.array(sweepBsv21InputSchema).describe('Token UTXOs to sweep (must all be same tokenId)'),
16
- wif: z.string().describe('WIF private key controlling the inputs'),
17
- })
18
+ inputs: z
19
+ .array(sweepBsv21InputSchema)
20
+ .describe("Token UTXOs to sweep (must all be same tokenId)"),
21
+ wif: z.string().describe("WIF private key controlling the inputs"),
22
+ });
18
23
 
19
24
  export function registerSweepBsv21Tool(
20
25
  server: McpServer,
21
26
  ctx: OneSatContext | undefined,
22
27
  ) {
23
28
  server.tool(
24
- 'wallet_sweepBsv21',
25
- 'Sweep BSV21 tokens from an external WIF private key into the wallet',
29
+ "wallet_sweepBsv21",
30
+ "Sweep BSV21 tokens from an external WIF private key into the wallet",
26
31
  { ...sweepBsv21Schema.shape },
27
32
  async ({ inputs, wif }) => {
28
33
  if (!ctx) {
29
34
  return {
30
35
  content: [
31
36
  {
32
- type: 'text' as const,
33
- text: 'BRC-100 wallet context not available',
37
+ type: "text" as const,
38
+ text: "BRC-100 wallet context not available",
34
39
  },
35
40
  ],
36
41
  isError: true,
37
- }
42
+ };
38
43
  }
39
44
 
40
45
  try {
41
- const result = await sweepBsv21.execute(ctx, { inputs, wif })
46
+ assertBroadcastAllowed("wallet_sweepBsv21");
47
+ const key = PrivateKey.fromWif(wif);
48
+ const result = await sweepBsv21.execute(ctx, {
49
+ inputs,
50
+ keys: inputs.map(() => key),
51
+ });
42
52
 
43
53
  if (result.error) {
44
54
  return {
45
- content: [{ type: 'text' as const, text: result.error }],
55
+ content: [
56
+ { type: "text" as const, text: redactKeyMaterial(result.error) },
57
+ ],
46
58
  isError: true,
47
- }
59
+ };
48
60
  }
49
61
 
50
62
  return {
51
63
  content: [
52
64
  {
53
- type: 'text' as const,
54
- text: JSON.stringify(result, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2),
65
+ type: "text" as const,
66
+ text: redactUnknown(result, 2),
55
67
  },
56
68
  ],
57
- }
69
+ };
58
70
  } catch (error) {
59
71
  return {
60
72
  content: [
61
73
  {
62
- type: 'text' as const,
63
- text: error instanceof Error ? error.message : String(error),
74
+ type: "text" as const,
75
+ text: redactKeyMaterial(
76
+ error instanceof Error ? error.message : String(error),
77
+ ),
64
78
  },
65
79
  ],
66
80
  isError: true,
67
- }
81
+ };
68
82
  }
69
83
  },
70
- )
84
+ );
71
85
  }
@@ -1,69 +1,81 @@
1
- import type { OneSatContext } from '@1sat/actions'
2
- import { sweepOrdinals } 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 { sweepOrdinals } from "@1sat/actions";
3
+ import { PrivateKey } from "@bsv/sdk";
4
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { z } from "zod";
6
+ import { assertBroadcastAllowed } from "../../utils/broadcastGuard";
7
+ import { redactKeyMaterial, redactUnknown } from "../../utils/redact";
5
8
 
6
9
  const sweepInputSchema = z.object({
7
- outpoint: z.string().describe('Outpoint (txid_vout)'),
8
- satoshis: z.number().int().describe('Satoshis (should be 1)'),
9
- lockingScript: z.string().describe('Locking script hex'),
10
- })
10
+ outpoint: z.string().describe("Outpoint (txid_vout)"),
11
+ satoshis: z.number().int().describe("Satoshis (should be 1)"),
12
+ lockingScript: z.string().describe("Locking script hex"),
13
+ });
11
14
 
12
15
  const sweepOrdinalsSchema = z.object({
13
- inputs: z.array(sweepInputSchema).describe('Ordinal UTXOs to sweep'),
14
- wif: z.string().describe('WIF private key controlling the inputs'),
15
- })
16
+ inputs: z.array(sweepInputSchema).describe("Ordinal UTXOs to sweep"),
17
+ wif: z.string().describe("WIF private key controlling the inputs"),
18
+ });
16
19
 
17
20
  export function registerSweepOrdinalsTool(
18
21
  server: McpServer,
19
22
  ctx: OneSatContext | undefined,
20
23
  ) {
21
24
  server.tool(
22
- 'wallet_sweepOrdinals',
23
- 'Sweep ordinals from an external WIF private key into the wallet',
25
+ "wallet_sweepOrdinals",
26
+ "Sweep ordinals from an external WIF private key into the wallet",
24
27
  { ...sweepOrdinalsSchema.shape },
25
28
  async ({ inputs, wif }) => {
26
29
  if (!ctx) {
27
30
  return {
28
31
  content: [
29
32
  {
30
- type: 'text' as const,
31
- text: 'BRC-100 wallet context not available',
33
+ type: "text" as const,
34
+ text: "BRC-100 wallet context not available",
32
35
  },
33
36
  ],
34
37
  isError: true,
35
- }
38
+ };
36
39
  }
37
40
 
38
41
  try {
39
- const result = await sweepOrdinals.execute(ctx, { inputs, wif })
42
+ assertBroadcastAllowed("wallet_sweepOrdinals");
43
+ const key = PrivateKey.fromWif(wif);
44
+ const result = await sweepOrdinals.execute(ctx, {
45
+ inputs,
46
+ keys: inputs.map(() => key),
47
+ });
40
48
 
41
49
  if (result.error) {
42
50
  return {
43
- content: [{ type: 'text' as const, text: result.error }],
51
+ content: [
52
+ { type: "text" as const, text: redactKeyMaterial(result.error) },
53
+ ],
44
54
  isError: true,
45
- }
55
+ };
46
56
  }
47
57
 
48
58
  return {
49
59
  content: [
50
60
  {
51
- type: 'text' as const,
52
- text: JSON.stringify(result, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2),
61
+ type: "text" as const,
62
+ text: redactUnknown(result, 2),
53
63
  },
54
64
  ],
55
- }
65
+ };
56
66
  } catch (error) {
57
67
  return {
58
68
  content: [
59
69
  {
60
- type: 'text' as const,
61
- text: error instanceof Error ? error.message : String(error),
70
+ type: "text" as const,
71
+ text: redactKeyMaterial(
72
+ error instanceof Error ? error.message : String(error),
73
+ ),
62
74
  },
63
75
  ],
64
76
  isError: true,
65
- }
77
+ };
66
78
  }
67
79
  },
68
- )
80
+ );
69
81
  }
@@ -3,6 +3,7 @@ import type { PrivateKey } from "@bsv/sdk";
3
3
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  import { registerA2bPublishMcpTool } from "./a2bPublishMcp";
5
5
  import { registerBrc100Tools } from "./brc100";
6
+ import { registerCancelListingTool } from "./cancelListing";
6
7
  import { registerCreateOrdinalsTool } from "./createOrdinals";
7
8
  import { registerGatherCollectionInfoTool } from "./gatherCollectionInfo";
8
9
  import { registerGetAddressTool } from "./getAddress";
@@ -10,23 +11,22 @@ import { registerWalletGetBalanceTool } from "./getBalance";
10
11
  import { registerGetBsv21BalancesTool } from "./getBsv21Balances";
11
12
  import { registerGetLockDataTool } from "./getLockData";
12
13
  import { registerGetOrdinalsTool } from "./getOrdinals";
14
+ import { registerListOrdinalTool } from "./listOrdinal";
13
15
  import { registerListTokensTool } from "./listTokens";
16
+ import { registerLockBsvTool } from "./lockBsv";
14
17
  import { registerMintCollectionTool } from "./mintCollection";
18
+ import { registerOpnsDeregisterTool } from "./opnsDeregister";
19
+ import { registerOpnsRegisterTool } from "./opnsRegister";
15
20
  import { registerPurchaseListingTool } from "./purchaseListing";
16
21
  import { registerRefreshUtxosTool } from "./refreshUtxos";
17
- import { registerSendToAddressTool } from "./sendToAddress";
18
- import { registerSignBsmTool } from "./signBsm";
19
- import { registerTransferOrdTokenTool } from "./transferOrdToken";
20
- import { registerListOrdinalTool } from "./listOrdinal";
21
- import { registerCancelListingTool } from "./cancelListing";
22
22
  import { registerSendAllBsvTool } from "./sendAllBsv";
23
- import { registerLockBsvTool } from "./lockBsv";
24
- import { registerUnlockBsvTool } from "./unlockBsv";
25
- import { registerOpnsRegisterTool } from "./opnsRegister";
26
- import { registerOpnsDeregisterTool } from "./opnsDeregister";
23
+ import { registerSendBsvTool } from "./sendBsv";
24
+ import { registerSignBsmTool } from "./signBsm";
27
25
  import { registerSweepBsvTool } from "./sweepBsv";
28
- import { registerSweepOrdinalsTool } from "./sweepOrdinals";
29
26
  import { registerSweepBsv21Tool } from "./sweepBsv21";
27
+ import { registerSweepOrdinalsTool } from "./sweepOrdinals";
28
+ import { registerTransferOrdTokenTool } from "./transferOrdToken";
29
+ import { registerUnlockBsvTool } from "./unlockBsv";
30
30
  import type { Wallet } from "./wallet";
31
31
 
32
32
  export function registerWalletTools(
@@ -39,8 +39,7 @@ export function registerWalletTools(
39
39
  ctx?: OneSatContext;
40
40
  },
41
41
  ): void {
42
- // Register the wallet_sendToAddress tool
43
- registerSendToAddressTool(server, config.ctx);
42
+ registerSendBsvTool(server, config.ctx);
44
43
 
45
44
  // Register the wallet_getAddress tool
46
45
  registerGetAddressTool(server, config.ctx);
@@ -95,5 +94,4 @@ export function registerWalletTools(
95
94
  registerSweepBsvTool(server, config.ctx);
96
95
  registerSweepOrdinalsTool(server, config.ctx);
97
96
  registerSweepBsv21Tool(server, config.ctx);
98
-
99
97
  }
@@ -50,14 +50,16 @@ export async function executeTransaction<TArgs>(
50
50
  throw new Error("Could not get wallet address");
51
51
  }
52
52
 
53
- // Parse identity key if provided
53
+ // Absent means "do not use identity features". Present but unparseable
54
+ // means the caller meant to sign and the key is wrong, so proceeding
55
+ // would silently produce an unsigned transaction.
54
56
  let identityKey: PrivateKey | undefined;
55
57
  if (identityKeyWif) {
56
58
  try {
57
59
  identityKey = PrivateKey.fromWif(identityKeyWif);
58
60
  } catch (e) {
59
- console.warn(
60
- "Invalid IDENTITY_KEY_WIF provided, skipping identity features",
61
+ throw new Error(
62
+ `An identity key was supplied but is not a valid WIF: ${formatError(e)}`,
61
63
  );
62
64
  }
63
65
  }
@@ -96,8 +98,14 @@ export function getIdentityKeyFromEnv(): PrivateKey | undefined {
96
98
  try {
97
99
  return PrivateKey.fromWif(identityKeyWif);
98
100
  } catch (e) {
99
- console.warn("Invalid IDENTITY_KEY_WIF in environment:", formatError(e));
100
- return undefined;
101
+ // Unset means "do not sign" and is a valid configuration. Set but
102
+ // unparseable means the operator intended to sign and the key is wrong,
103
+ // so returning undefined here would silently publish unsigned data under
104
+ // no identity. Fail instead. The parse error carries no key material --
105
+ // @bsv/sdk reports "Invalid checksum" without echoing its input.
106
+ throw new Error(
107
+ `IDENTITY_KEY_WIF is set but is not a valid WIF: ${formatError(e)}`,
108
+ );
101
109
  }
102
110
  }
103
111