bsv-mcp 0.2.9 → 0.2.11

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 (40) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +26 -2
  3. package/dist/index.js +6777 -126216
  4. package/index.ts +287 -265
  5. package/package.json +4 -3
  6. package/tools/bap/generate.test.ts +17 -16
  7. package/tools/index.ts +8 -3
  8. package/tools/ordinals/getInscription.ts +15 -65
  9. package/tools/ordinals/getTokenByIdOrTicker.ts +14 -80
  10. package/tools/ordinals/index.ts +8 -7
  11. package/tools/ordinals/marketListings.ts +21 -188
  12. package/tools/ordinals/marketSales.ts +21 -127
  13. package/tools/ordinals/searchInscriptions.ts +21 -108
  14. package/tools/wallet/brc100.ts +572 -0
  15. package/tools/wallet/cancelListing.ts +80 -0
  16. package/tools/wallet/createOrdinals.ts +62 -143
  17. package/tools/wallet/getAddress.ts +19 -13
  18. package/tools/wallet/getBalance.ts +29 -25
  19. package/tools/wallet/getBsv21Balances.ts +28 -0
  20. package/tools/wallet/getLockData.ts +25 -0
  21. package/tools/wallet/getOrdinals.ts +35 -0
  22. package/tools/wallet/listOrdinal.ts +86 -0
  23. package/tools/wallet/listTokens.ts +28 -0
  24. package/tools/wallet/lockBsv.ts +69 -0
  25. package/tools/wallet/opnsDeregister.ts +80 -0
  26. package/tools/wallet/opnsRegister.ts +80 -0
  27. package/tools/wallet/purchaseListing.ts +89 -290
  28. package/tools/wallet/refreshUtxos.ts +21 -32
  29. package/tools/wallet/schemas.ts +7 -11
  30. package/tools/wallet/sendAllBsv.ts +64 -0
  31. package/tools/wallet/sendToAddress.ts +46 -47
  32. package/tools/wallet/signBsm.ts +29 -0
  33. package/tools/wallet/sweepBsv.ts +70 -0
  34. package/tools/wallet/sweepBsv21.ts +71 -0
  35. package/tools/wallet/sweepOrdinals.ts +69 -0
  36. package/tools/wallet/tools.ts +50 -88
  37. package/tools/wallet/transferOrdToken.ts +137 -129
  38. package/tools/wallet/unlockBsv.ts +57 -0
  39. package/tools/wallet/a2bPublishAgent.ts +0 -307
  40. package/tools/wallet/sendOrdinals.ts +0 -160
@@ -1,168 +1,87 @@
1
- import { PrivateKey } from "@bsv/sdk";
2
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
4
- import type {
5
- ChangeResult,
6
- CreateOrdinalsCollectionItemMetadata,
7
- CreateOrdinalsCollectionMetadata,
8
- CreateOrdinalsConfig,
9
- Destination,
10
- Inscription,
11
- LocalSigner,
12
- PreMAP,
13
- } from "js-1sat-ord";
14
- import { createOrdinals } from "js-1sat-ord";
15
- import { z } from "zod";
16
- import { V5Broadcaster } from "../../utils/broadcaster";
17
- import type { Wallet } from "./wallet";
1
+ import type { OneSatContext } from '@1sat/actions'
2
+ import { inscribe } from '@1sat/actions'
3
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
4
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'
5
+ import { z } from 'zod'
18
6
 
19
- /**
20
- * Schema for the createOrdinals tool arguments.
21
- * This is a simplified interface compared to the full CreateOrdinalsConfig
22
- * in js-1sat-ord. The wallet will provide UTXOs, private key, etc.
23
- */
24
7
  export const createOrdinalsArgsSchema = z.object({
25
- // Base64-encoded data to inscribe
26
- dataB64: z.string().describe("Base64-encoded content to inscribe"),
27
- // Content type (e.g., "image/jpeg", "text/plain", etc.)
28
- contentType: z.string().describe("MIME type of the content"),
29
- // Optional destination address (if not provided, uses the wallet's address)
8
+ dataB64: z.string().describe('Base64-encoded content to inscribe'),
9
+ contentType: z.string().describe('MIME type of the content'),
30
10
  destinationAddress: z
31
11
  .string()
32
12
  .optional()
33
- .describe("Optional destination address for the ordinal"),
34
- // Optional metadata for the inscription
13
+ .describe('Optional destination address for the ordinal'),
35
14
  metadata: z
36
- .any()
15
+ .record(z.string(), z.string())
37
16
  .optional()
38
- .describe("Optional MAP metadata for the inscription"),
39
- });
17
+ .describe('Optional MAP metadata for the inscription'),
18
+ signWithBAP: z
19
+ .boolean()
20
+ .optional()
21
+ .describe('Sign with BAP identity (Sigma protocol). Uses anchor+inscription two-step flow.'),
22
+ })
40
23
 
41
- export type CreateOrdinalsArgs = z.infer<typeof createOrdinalsArgsSchema>;
24
+ export type CreateOrdinalsArgs = z.infer<typeof createOrdinalsArgsSchema>
42
25
 
43
- /**
44
- * Registers the wallet_createOrdinals tool for minting ordinals/inscriptions
45
- */
46
- export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
26
+ export function registerCreateOrdinalsTool(
27
+ server: McpServer,
28
+ ctx: OneSatContext | undefined,
29
+ ) {
47
30
  server.tool(
48
- "wallet_createOrdinals",
49
- "Creates and inscribes ordinals (NFTs) on the Bitcoin SV blockchain. This tool lets you mint new digital artifacts by encoding data directly into the blockchain. Supports various content types including images, text, JSON, and HTML. The tool handles transaction creation, fee calculation, and broadcasting.",
31
+ 'wallet_createOrdinals',
32
+ 'Creates and inscribes ordinals (NFTs) on the Bitcoin SV blockchain. This tool lets you mint new digital artifacts by encoding data directly into the blockchain. Supports various content types including images, text, JSON, and HTML. The tool handles transaction creation, fee calculation, and broadcasting.',
50
33
  { ...createOrdinalsArgsSchema.shape },
51
- async ({ dataB64, contentType, destinationAddress, metadata }): Promise<CallToolResult> => {
52
- try {
53
- // Load optional identity key for sigma signing
54
- const identityKeyWif = process.env.IDENTITY_KEY_WIF;
55
- let identityPk: PrivateKey | undefined;
56
- if (identityKeyWif) {
57
- try {
58
- identityPk = PrivateKey.fromWif(identityKeyWif);
59
- } catch (e) {
60
- console.warn(
61
- "Warning: Invalid IDENTITY_KEY_WIF environment variable; sigma signing disabled",
62
- e,
63
- );
64
- }
65
- }
66
-
67
- // 1. Get private key from wallet
68
- const paymentPk = wallet.getPrivateKey();
69
- if (!paymentPk) {
70
- throw new Error("No private key available in wallet");
71
- }
72
-
73
- // 2. Get payment UTXOs from wallet
74
- const { paymentUtxos } = await wallet.getUtxos();
75
- if (!paymentUtxos || paymentUtxos.length === 0) {
76
- throw new Error(
77
- "No payment UTXOs available to fund this inscription",
78
- );
79
- }
80
-
81
- // 3. Get the wallet address for change/destination if not provided
82
- const walletAddress = paymentPk.toAddress().toString();
83
-
84
- // 4. Create the inscription object
85
- const inscription: Inscription = {
86
- dataB64: dataB64,
87
- contentType: contentType,
88
- };
89
-
90
- // 5. Create the destination
91
- const destinations: Destination[] = [
92
- {
93
- address: destinationAddress || walletAddress,
94
- inscription,
95
- },
96
- ];
97
-
98
- const createOrdinalsConfig: CreateOrdinalsConfig = {
99
- utxos: paymentUtxos,
100
- destinations,
101
- paymentPk,
102
- changeAddress: walletAddress,
103
- metaData: metadata as
104
- | PreMAP
105
- | CreateOrdinalsCollectionMetadata
106
- | CreateOrdinalsCollectionItemMetadata,
107
- };
108
-
109
- if (identityPk) {
110
- createOrdinalsConfig.signer = {
111
- idKey: identityPk,
112
- } as LocalSigner;
34
+ async ({ dataB64, contentType, destinationAddress, metadata, signWithBAP }): Promise<CallToolResult> => {
35
+ if (!ctx) {
36
+ return {
37
+ content: [
38
+ {
39
+ type: 'text',
40
+ text: 'Wallet not initialized. Please configure a wallet before creating ordinals.',
41
+ },
42
+ ],
43
+ isError: true,
113
44
  }
45
+ }
114
46
 
115
- // 6. Create and broadcast the transaction
116
- const result = await createOrdinals(createOrdinalsConfig);
117
-
118
- const changeResult = result as ChangeResult;
119
-
120
- // 7. Optionally sign with identity key and broadcast the transaction
121
-
122
- const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
123
- if (!disableBroadcasting) {
124
- const broadcaster = new V5Broadcaster();
125
- await changeResult.tx.broadcast(broadcaster);
126
-
127
- // 8. Refresh the wallet's UTXOs after spending
128
- try {
129
- await wallet.refreshUtxos();
130
- } catch (refreshError) {
131
- console.warn(
132
- "Failed to refresh UTXOs after transaction:",
133
- refreshError,
134
- );
135
- }
136
-
137
- // 9. Return transaction details
47
+ try {
48
+ const result = await inscribe.execute(ctx, {
49
+ base64Content: dataB64,
50
+ contentType,
51
+ map: metadata,
52
+ signWithBAP,
53
+ })
54
+
55
+ if (result.error) {
138
56
  return {
139
- content: [
140
- {
141
- type: "text",
142
- text: JSON.stringify({
143
- txid: changeResult.tx.id("hex"),
144
- spentOutpoints: changeResult.spentOutpoints,
145
- payChange: changeResult.payChange,
146
- inscriptionAddress: destinationAddress || walletAddress,
147
- contentType: contentType,
148
- }),
149
- },
150
- ],
151
- };
57
+ content: [{ type: 'text', text: result.error }],
58
+ isError: true,
59
+ }
152
60
  }
153
61
 
154
62
  return {
155
63
  content: [
156
64
  {
157
- type: "text",
158
- text: changeResult.tx.toHex(),
65
+ type: 'text',
66
+ text: JSON.stringify({
67
+ txid: result.txid,
68
+ rawtx: result.rawtx,
69
+ contentType,
70
+ }),
159
71
  },
160
72
  ],
161
- };
73
+ }
162
74
  } catch (err: unknown) {
163
- const msg = err instanceof Error ? err.message : String(err);
164
- return { content: [{ type: "text", text: msg }], isError: true };
75
+ return {
76
+ content: [
77
+ {
78
+ type: 'text',
79
+ text: err instanceof Error ? err.message : String(err),
80
+ },
81
+ ],
82
+ isError: true,
83
+ }
165
84
  }
166
85
  },
167
- );
86
+ )
168
87
  }
@@ -1,34 +1,40 @@
1
+ import {
2
+ deriveDepositAddresses,
3
+ type OneSatContext,
4
+ } from "@1sat/actions";
1
5
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { Wallet } from "./wallet";
6
+
7
+ const MCP_ADDRESS_PREFIX = "mcp";
3
8
 
4
9
  /**
5
- * Register the tool to get the wallet address
6
- * @param server The MCP server instance
7
- * @param wallet The initialized custom Wallet instance
10
+ * Register the tool to get the wallet's BRC-29 deposit address
8
11
  */
9
12
  export function registerGetAddressTool(
10
13
  server: McpServer,
11
- wallet: Wallet,
14
+ ctx?: OneSatContext,
12
15
  ): void {
13
16
  server.tool(
14
17
  "wallet_getAddress",
15
- "Retrieves the current wallet's Bitcoin SV address. This address can be used to receive BSV, ordinals, or tokens, and is derived from the wallet's private key.",
18
+ "Retrieves the wallet's BRC-29 deposit address derived for MCP. This address can receive BSV, ordinals, or tokens via external payments.",
16
19
  {},
17
20
  async () => {
18
21
  try {
19
- const address = wallet.getAddress();
20
-
21
- if (!address) {
22
- throw new Error(
23
- "Could not retrieve address from wallet instance. Key might be missing or getAddress method failed.",
24
- );
22
+ if (!ctx) {
23
+ throw new Error("BRC-100 wallet context not available");
25
24
  }
26
25
 
26
+ const { derivations } = await deriveDepositAddresses.execute(ctx, {
27
+ prefix: MCP_ADDRESS_PREFIX,
28
+ });
29
+
27
30
  return {
28
31
  content: [
29
32
  {
30
33
  type: "text",
31
- text: JSON.stringify({ address, status: "ok" }),
34
+ text: JSON.stringify({
35
+ address: derivations[0].address,
36
+ status: "ok",
37
+ }),
32
38
  },
33
39
  ],
34
40
  };
@@ -1,54 +1,58 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { toBitcoin } from "satoshi-token"; // For converting satoshis to BSV string
3
- import type { Wallet } from "./wallet";
1
+ import type { OneSatContext } from '@1sat/actions'
2
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
3
+ import { toBitcoin } from 'satoshi-token'
4
4
 
5
- /**
6
- * Registers the wallet_getBalance tool
7
- */
8
5
  export function registerWalletGetBalanceTool(
9
6
  server: McpServer,
10
- wallet: Wallet,
7
+ ctx: OneSatContext | undefined,
11
8
  ) {
12
9
  server.tool(
13
- "wallet_getBalance",
14
- "Retrieves the current BSV balance for the wallet based on its UTXOs.",
10
+ 'wallet_getBalance',
11
+ 'Retrieves the current BSV balance for the wallet.',
15
12
  {},
16
13
  async () => {
17
- try {
18
- const { paymentUtxos } = await wallet.getUtxos();
19
-
20
- let totalSatoshis = 0;
21
- if (paymentUtxos && paymentUtxos.length > 0) {
22
- for (const utxo of paymentUtxos) {
23
- totalSatoshis += utxo.satoshis || 0;
24
- }
14
+ if (!ctx) {
15
+ return {
16
+ content: [
17
+ {
18
+ type: 'text',
19
+ text: 'Wallet not initialized. Please configure a wallet before checking balance.',
20
+ },
21
+ ],
22
+ isError: true,
25
23
  }
24
+ }
26
25
 
27
- const bsvAmount = toBitcoin(totalSatoshis); // Convert to BSV string
26
+ try {
27
+ const result = await ctx.wallet.listOutputs({ basket: 'default' })
28
+ const totalSatoshis = result.outputs.reduce(
29
+ (sum, output) => sum + output.satoshis,
30
+ 0,
31
+ )
28
32
 
29
33
  return {
30
34
  content: [
31
35
  {
32
- type: "text",
36
+ type: 'text',
33
37
  text: JSON.stringify({
34
38
  satoshis: totalSatoshis,
35
- bsv: bsvAmount,
36
- utxoCount: paymentUtxos ? paymentUtxos.length : 0,
39
+ bsv: toBitcoin(totalSatoshis),
40
+ utxoCount: result.totalOutputs,
37
41
  }),
38
42
  },
39
43
  ],
40
- };
44
+ }
41
45
  } catch (error) {
42
46
  return {
43
47
  content: [
44
48
  {
45
- type: "text",
49
+ type: 'text',
46
50
  text: error instanceof Error ? error.message : String(error),
47
51
  },
48
52
  ],
49
53
  isError: true,
50
- };
54
+ }
51
55
  }
52
56
  },
53
- );
57
+ )
54
58
  }
@@ -0,0 +1,28 @@
1
+ import { getBsv21Balances, type OneSatContext } from '@1sat/actions'
2
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
3
+
4
+ export function registerGetBsv21BalancesTool(
5
+ server: McpServer,
6
+ ctx?: OneSatContext,
7
+ ) {
8
+ server.tool(
9
+ 'wallet_getBsv21Balances',
10
+ 'Get aggregated BSV21 token balances grouped by token ID',
11
+ {},
12
+ async () => {
13
+ try {
14
+ if (!ctx) throw new Error('BRC-100 wallet context not available')
15
+ const result = await getBsv21Balances.execute(ctx, {})
16
+ return {
17
+ content: [{
18
+ type: 'text',
19
+ text: JSON.stringify(result, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2),
20
+ }],
21
+ }
22
+ } catch (err: unknown) {
23
+ const msg = err instanceof Error ? err.message : String(err)
24
+ return { content: [{ type: 'text', text: msg }], isError: true }
25
+ }
26
+ },
27
+ )
28
+ }
@@ -0,0 +1,25 @@
1
+ import { getLockData, type OneSatContext } from '@1sat/actions'
2
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
3
+
4
+ export function registerGetLockDataTool(
5
+ server: McpServer,
6
+ ctx?: OneSatContext,
7
+ ) {
8
+ server.tool(
9
+ 'wallet_getLockData',
10
+ 'Get summary of time-locked BSV (total, unlockable, next unlock height)',
11
+ {},
12
+ async () => {
13
+ try {
14
+ if (!ctx) throw new Error('BRC-100 wallet context not available')
15
+ const result = await getLockData.execute(ctx, {})
16
+ return {
17
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
18
+ }
19
+ } catch (err: unknown) {
20
+ const msg = err instanceof Error ? err.message : String(err)
21
+ return { content: [{ type: 'text', text: msg }], isError: true }
22
+ }
23
+ },
24
+ )
25
+ }
@@ -0,0 +1,35 @@
1
+ import { getOrdinals, type OneSatContext } from '@1sat/actions'
2
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
3
+ import { z } from 'zod'
4
+
5
+ export function registerGetOrdinalsTool(
6
+ server: McpServer,
7
+ ctx?: OneSatContext,
8
+ ) {
9
+ server.tool(
10
+ 'wallet_getOrdinals',
11
+ 'List ordinals/inscriptions in the wallet with metadata',
12
+ {
13
+ limit: z.number().int().optional().describe('Max number of results to return'),
14
+ offset: z.number().int().optional().describe('Number of results to skip'),
15
+ },
16
+ async (params) => {
17
+ try {
18
+ if (!ctx) throw new Error('BRC-100 wallet context not available')
19
+ const result = await getOrdinals.execute(ctx, params)
20
+ const outputs = result.outputs.map((o: any) => ({
21
+ outpoint: o.outpoint,
22
+ satoshis: o.satoshis,
23
+ tags: o.tags,
24
+ customInstructions: o.customInstructions,
25
+ }))
26
+ return {
27
+ content: [{ type: 'text', text: JSON.stringify(outputs, null, 2) }],
28
+ }
29
+ } catch (err: unknown) {
30
+ const msg = err instanceof Error ? err.message : String(err)
31
+ return { content: [{ type: 'text', text: msg }], isError: true }
32
+ }
33
+ },
34
+ )
35
+ }
@@ -0,0 +1,86 @@
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
+ })
13
+
14
+ 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())
20
+ .describe(
21
+ "BEEF bytes from listOutputs (include: 'entire transactions')",
22
+ ),
23
+ price: z.number().describe('Price in satoshis'),
24
+ payAddress: z
25
+ .string()
26
+ .describe('Address to receive payment on purchase'),
27
+ })
28
+
29
+ export function registerListOrdinalTool(
30
+ server: McpServer,
31
+ ctx: OneSatContext | undefined,
32
+ ) {
33
+ server.tool(
34
+ 'wallet_listOrdinal',
35
+ 'List an ordinal for sale on the marketplace',
36
+ { ...listOrdinalArgsSchema.shape },
37
+ async ({ ordinal, inputBEEF, price, payAddress }) => {
38
+ if (!ctx) {
39
+ return {
40
+ content: [
41
+ {
42
+ type: 'text',
43
+ text: 'Wallet not initialized. Please configure a wallet before listing.',
44
+ },
45
+ ],
46
+ isError: true,
47
+ }
48
+ }
49
+
50
+ try {
51
+ const result = await listOrdinal.execute(ctx, {
52
+ ordinal: ordinal as Parameters<typeof listOrdinal.execute>[1]['ordinal'],
53
+ inputBEEF,
54
+ price,
55
+ payAddress,
56
+ })
57
+
58
+ if (result.error) {
59
+ return {
60
+ content: [{ type: 'text', text: result.error }],
61
+ isError: true,
62
+ }
63
+ }
64
+
65
+ return {
66
+ content: [
67
+ {
68
+ type: 'text',
69
+ text: JSON.stringify(result, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2),
70
+ },
71
+ ],
72
+ }
73
+ } catch (err: unknown) {
74
+ return {
75
+ content: [
76
+ {
77
+ type: 'text',
78
+ text: err instanceof Error ? err.message : String(err),
79
+ },
80
+ ],
81
+ isError: true,
82
+ }
83
+ }
84
+ },
85
+ )
86
+ }
@@ -0,0 +1,28 @@
1
+ import { listTokens, type OneSatContext } from '@1sat/actions'
2
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
3
+ import { z } from 'zod'
4
+
5
+ export function registerListTokensTool(
6
+ server: McpServer,
7
+ ctx?: OneSatContext,
8
+ ) {
9
+ server.tool(
10
+ 'wallet_listTokens',
11
+ 'List BSV21 token outputs in the wallet',
12
+ {
13
+ limit: z.number().int().optional().describe('Max number of results to return'),
14
+ },
15
+ async (params) => {
16
+ try {
17
+ if (!ctx) throw new Error('BRC-100 wallet context not available')
18
+ const result = await listTokens.execute(ctx, params)
19
+ return {
20
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
21
+ }
22
+ } catch (err: unknown) {
23
+ const msg = err instanceof Error ? err.message : String(err)
24
+ return { content: [{ type: 'text', text: msg }], isError: true }
25
+ }
26
+ },
27
+ )
28
+ }
@@ -0,0 +1,69 @@
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
+
6
+ const lockBsvArgsSchema = z.object({
7
+ requests: z
8
+ .array(
9
+ z.object({
10
+ satoshis: z.number().describe('Amount in satoshis to lock'),
11
+ until: z.number().describe('Block height until which to lock'),
12
+ }),
13
+ )
14
+ .describe('Array of lock requests'),
15
+ })
16
+
17
+ export function registerLockBsvTool(
18
+ server: McpServer,
19
+ ctx: OneSatContext | undefined,
20
+ ) {
21
+ server.tool(
22
+ 'wallet_lockBsv',
23
+ 'Lock BSV until a specific block height',
24
+ { ...lockBsvArgsSchema.shape },
25
+ async ({ requests }) => {
26
+ if (!ctx) {
27
+ return {
28
+ content: [
29
+ {
30
+ type: 'text',
31
+ text: 'Wallet not initialized. Please configure a wallet before locking.',
32
+ },
33
+ ],
34
+ isError: true,
35
+ }
36
+ }
37
+
38
+ try {
39
+ const result = await lockBsv.execute(ctx, { requests })
40
+
41
+ if (result.error) {
42
+ return {
43
+ content: [{ type: 'text', text: result.error }],
44
+ isError: true,
45
+ }
46
+ }
47
+
48
+ return {
49
+ content: [
50
+ {
51
+ type: 'text',
52
+ text: JSON.stringify(result, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2),
53
+ },
54
+ ],
55
+ }
56
+ } catch (err: unknown) {
57
+ return {
58
+ content: [
59
+ {
60
+ type: 'text',
61
+ text: err instanceof Error ? err.message : String(err),
62
+ },
63
+ ],
64
+ isError: true,
65
+ }
66
+ }
67
+ },
68
+ )
69
+ }