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,156 +1,164 @@
1
- import { PrivateKey } from "@bsv/sdk";
2
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import {
4
- type Distribution,
5
- type LocalSigner,
6
- OneSatBroadcaster,
7
- type Payment,
8
- selectTokenUtxos,
9
- type TokenChangeResult,
10
- TokenInputMode,
11
- TokenSelectionStrategy,
12
- TokenType,
13
- type TokenUtxo,
14
- type TransferOrdTokensConfig,
15
- transferOrdTokens,
16
- type Utxo,
17
- } from "js-1sat-ord";
18
- import { z } from "zod";
19
- import { V5Broadcaster } from "../../utils/broadcaster";
20
- import type { Wallet } from "./wallet";
1
+ import type { OneSatContext } from '@1sat/actions'
2
+ import { sendBsv21, transferOrdinals } 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
+ })
21
13
 
22
- // Schema for BSV-20/BSV-21 token transfer arguments
23
14
  export const transferOrdTokenArgsSchema = z.object({
24
- protocol: z.enum(["bsv-20", "bsv-21"]),
25
- tokenID: z.string(),
26
- sendAmount: z.number(),
27
- paymentUtxos: z.array(
28
- z.object({
29
- txid: z.string(),
30
- vout: z.number(),
31
- satoshis: z.number(),
32
- script: z.string(),
33
- }),
34
- ),
35
- tokenUtxos: z.array(
36
- z.object({
37
- txid: z.string(),
38
- vout: z.number(),
39
- satoshis: z.literal(1),
40
- script: z.string(),
41
- amt: z.string(),
42
- id: z.string(),
43
- }),
15
+ type: z.enum(['ordinal', 'bsv21']).describe(
16
+ "'ordinal' to transfer an inscription/NFT, 'bsv21' to send fungible BSV21 tokens",
44
17
  ),
45
- distributions: z.array(z.object({ address: z.string(), tokens: z.number() })),
46
- decimals: z.number(),
47
- additionalPayments: z
48
- .array(z.object({ to: z.string(), amount: z.number() }))
49
- .optional(),
50
- });
51
- export type TransferOrdTokenArgs = z.infer<typeof transferOrdTokenArgsSchema>;
18
+ // ordinal fields
19
+ ordinal: walletOutputSchema
20
+ .optional()
21
+ .describe(
22
+ "WalletOutput of the ordinal to transfer (from wallet_getOrdinals). Required when type='ordinal'",
23
+ ),
24
+ inputBEEF: z
25
+ .array(z.number())
26
+ .optional()
27
+ .describe(
28
+ "BEEF bytes from listOutputs (include: 'entire transactions'). Required when type='ordinal'",
29
+ ),
30
+ // bsv21 fields
31
+ tokenId: z
32
+ .string()
33
+ .optional()
34
+ .describe("Token ID (txid_vout format). Required when type='bsv21'"),
35
+ amount: z
36
+ .string()
37
+ .optional()
38
+ .describe("Amount of tokens to send as a string integer. Required when type='bsv21'"),
39
+ // shared destination fields
40
+ address: z.string().optional().describe('Recipient P2PKH address'),
41
+ counterparty: z
42
+ .string()
43
+ .optional()
44
+ .describe('Recipient identity public key (hex)'),
45
+ })
46
+
47
+ export type TransferOrdTokenArgs = z.infer<typeof transferOrdTokenArgsSchema>
52
48
 
53
- /**
54
- * Register the wallet_transferOrdToken tool for transferring BSV tokens.
55
- */
56
49
  export function registerTransferOrdTokenTool(
57
50
  server: McpServer,
58
- wallet: Wallet,
51
+ ctx: OneSatContext | undefined,
59
52
  ) {
60
53
  server.tool(
61
- "wallet_transferOrdToken",
62
- "Transfers BSV-20 or BSV-21 tokens from your wallet via js-1sat-ord transferOrdTokens.",
54
+ 'wallet_transferOrdToken',
55
+ "Transfer an ordinal inscription or send BSV21 fungible tokens. Use type='ordinal' to transfer an NFT/inscription (requires the WalletOutput from wallet_getOrdinals and the BEEF). Use type='bsv21' to send fungible tokens by token ID and amount.",
63
56
  { ...transferOrdTokenArgsSchema.shape },
64
- async ({ protocol, tokenID, sendAmount, paymentUtxos, tokenUtxos, distributions, decimals, additionalPayments }) => {
65
- try {
66
- // fetch keys
67
- const paymentPk = wallet.getPaymentKey();
68
- if (!paymentPk) throw new Error("No private key available");
69
- const ordPk = paymentPk;
70
- const changeAddress = paymentPk.toAddress().toString();
71
- const ordAddress = changeAddress;
57
+ async ({ type, ordinal, inputBEEF, tokenId, amount, address, counterparty }) => {
58
+ if (!ctx) {
59
+ return {
60
+ content: [
61
+ {
62
+ type: 'text',
63
+ text: 'Wallet not initialized. Please configure a wallet before transferring.',
64
+ },
65
+ ],
66
+ isError: true,
67
+ }
68
+ }
72
69
 
73
- // select token UTXOs
74
- const { selectedUtxos: inputTokens } = selectTokenUtxos(
75
- tokenUtxos as TokenUtxo[],
76
- sendAmount,
77
- decimals,
78
- {
79
- inputStrategy: TokenSelectionStrategy.SmallestFirst,
80
- outputStrategy: TokenSelectionStrategy.LargestFirst,
81
- },
82
- );
70
+ try {
71
+ if (type === 'ordinal') {
72
+ if (!ordinal) {
73
+ return {
74
+ content: [{ type: 'text', text: "ordinal is required when type='ordinal'" }],
75
+ isError: true,
76
+ }
77
+ }
78
+ if (!inputBEEF) {
79
+ return {
80
+ content: [{ type: 'text', text: "inputBEEF is required when type='ordinal'" }],
81
+ isError: true,
82
+ }
83
+ }
84
+ if (!address && !counterparty) {
85
+ return {
86
+ content: [{ type: 'text', text: 'address or counterparty is required' }],
87
+ isError: true,
88
+ }
89
+ }
83
90
 
84
- // build config
85
- const config: TransferOrdTokensConfig = {
86
- protocol:
87
- protocol === "bsv-20" ? TokenType.BSV20 : TokenType.BSV21,
88
- tokenID,
89
- utxos: paymentUtxos as Utxo[],
90
- inputTokens,
91
- distributions: distributions as Distribution[],
92
- tokenChangeAddress: ordAddress,
93
- changeAddress,
94
- paymentPk,
95
- ordPk,
96
- additionalPayments: (additionalPayments as Payment[]) || [],
97
- decimals,
98
- inputMode: TokenInputMode.Needed,
99
- splitConfig: {
100
- outputs: inputTokens.length === 1 ? 2 : 1,
101
- threshold: sendAmount,
102
- },
103
- };
91
+ const result = await transferOrdinals.execute(ctx, {
92
+ transfers: [
93
+ {
94
+ ordinal: ordinal as Parameters<typeof transferOrdinals.execute>[1]['transfers'][0]['ordinal'],
95
+ address,
96
+ counterparty,
97
+ },
98
+ ],
99
+ inputBEEF,
100
+ })
104
101
 
105
- const identityPk = process.env.IDENTITY_KEY_WIF
106
- ? PrivateKey.fromWif(process.env.IDENTITY_KEY_WIF)
107
- : undefined;
102
+ if (result.error) {
103
+ return {
104
+ content: [{ type: 'text', text: result.error }],
105
+ isError: true,
106
+ }
107
+ }
108
108
 
109
- if (identityPk) {
110
- config.signer = {
111
- idKey: identityPk,
112
- } as LocalSigner;
109
+ return {
110
+ content: [{ type: 'text', text: JSON.stringify({ txid: result.txid }) }],
111
+ }
113
112
  }
114
113
 
115
- // execute transfer
116
- const result: TokenChangeResult = await transferOrdTokens(config);
117
- const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
118
- if (!disableBroadcasting) {
119
- const broadcaster = new V5Broadcaster();
120
- await result.tx.broadcast(broadcaster);
114
+ // bsv21
115
+ if (!tokenId) {
116
+ return {
117
+ content: [{ type: 'text', text: "tokenId is required when type='bsv21'" }],
118
+ isError: true,
119
+ }
120
+ }
121
+ if (!amount) {
122
+ return {
123
+ content: [{ type: 'text', text: "amount is required when type='bsv21'" }],
124
+ isError: true,
125
+ }
126
+ }
127
+ if (!address && !counterparty) {
128
+ return {
129
+ content: [{ type: 'text', text: 'address or counterparty is required' }],
130
+ isError: true,
131
+ }
132
+ }
121
133
 
122
- // refresh UTXOs
123
- try {
124
- await wallet.refreshUtxos();
125
- } catch {}
134
+ const result = await sendBsv21.execute(ctx, {
135
+ tokenId,
136
+ amount,
137
+ address,
138
+ counterparty,
139
+ })
126
140
 
127
- // respond
141
+ if (result.error) {
128
142
  return {
129
- content: [
130
- {
131
- type: "text",
132
- text: JSON.stringify({
133
- txid: result.tx.id("hex"),
134
- spentOutpoints: result.spentOutpoints,
135
- payChange: result.payChange,
136
- tokenChange: result.tokenChange,
137
- }),
138
- },
139
- ],
140
- };
143
+ content: [{ type: 'text', text: result.error }],
144
+ isError: true,
145
+ }
141
146
  }
147
+
148
+ return {
149
+ content: [{ type: 'text', text: JSON.stringify({ txid: result.txid }) }],
150
+ }
151
+ } catch (err: unknown) {
142
152
  return {
143
153
  content: [
144
154
  {
145
- type: "text",
146
- text: result.tx.toHex(),
155
+ type: 'text',
156
+ text: err instanceof Error ? err.message : String(err),
147
157
  },
148
158
  ],
149
- };
150
- } catch (err: unknown) {
151
- const msg = err instanceof Error ? err.message : String(err);
152
- return { content: [{ type: "text", text: msg }], isError: true };
159
+ isError: true,
160
+ }
153
161
  }
154
162
  },
155
- );
163
+ )
156
164
  }
@@ -0,0 +1,57 @@
1
+ import type { OneSatContext } from '@1sat/actions'
2
+ import { unlockBsv } from '@1sat/actions'
3
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
4
+
5
+ export function registerUnlockBsvTool(
6
+ server: McpServer,
7
+ ctx: OneSatContext | undefined,
8
+ ) {
9
+ server.tool(
10
+ 'wallet_unlockBsv',
11
+ 'Unlock all matured time-locked BSV',
12
+ {},
13
+ async () => {
14
+ if (!ctx) {
15
+ return {
16
+ content: [
17
+ {
18
+ type: 'text',
19
+ text: 'Wallet not initialized. Please configure a wallet before unlocking.',
20
+ },
21
+ ],
22
+ isError: true,
23
+ }
24
+ }
25
+
26
+ try {
27
+ const result = await unlockBsv.execute(ctx, {})
28
+
29
+ if (result.error) {
30
+ return {
31
+ content: [{ type: 'text', text: result.error }],
32
+ isError: true,
33
+ }
34
+ }
35
+
36
+ return {
37
+ content: [
38
+ {
39
+ type: 'text',
40
+ text: JSON.stringify(result, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2),
41
+ },
42
+ ],
43
+ }
44
+ } catch (err: unknown) {
45
+ return {
46
+ content: [
47
+ {
48
+ type: 'text',
49
+ text: err instanceof Error ? err.message : String(err),
50
+ },
51
+ ],
52
+ isError: true,
53
+ }
54
+ }
55
+ },
56
+ )
57
+ }
@@ -1,307 +0,0 @@
1
- import { PrivateKey, Utils } from "@bsv/sdk";
2
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import type {
4
- ChangeResult,
5
- CreateOrdinalsConfig,
6
- Destination,
7
- Inscription,
8
- LocalSigner,
9
- PreMAP,
10
- } from "js-1sat-ord";
11
- import { createOrdinals } from "js-1sat-ord";
12
- import { z } from "zod";
13
- import packageJson from "../../package.json";
14
- import { V5Broadcaster } from "../../utils/broadcaster";
15
- import type { Wallet } from "./wallet";
16
-
17
- const { toArray, toBase64 } = Utils;
18
-
19
- // https://raw.githubusercontent.com/google/A2A/refs/heads/main/specification/json/a2a.json
20
-
21
- // A2A AgentCard schema (per A2A spec)
22
- const AgentCapabilitiesSchema = z.object({
23
- streaming: z.boolean().default(false),
24
- pushNotifications: z.boolean().default(false),
25
- stateTransitionHistory: z.boolean().default(false),
26
- });
27
- const AgentSkillSchema = z.object({
28
- id: z.string(),
29
- name: z.string(),
30
- description: z.string().nullable(),
31
- tags: z.array(z.string()).nullable(),
32
- examples: z.array(z.string()).nullable(),
33
- inputModes: z.array(z.string()).nullable(),
34
- outputModes: z.array(z.string()).nullable(),
35
- });
36
-
37
- // Provider per A2A spec
38
- const AgentProviderSchema = z
39
- .object({
40
- organization: z.string(),
41
- url: z.string().url().nullable().default(null),
42
- })
43
- .nullable()
44
- .default(null);
45
-
46
- // Authentication per A2A spec
47
- const AgentAuthenticationSchema = z
48
- .object({
49
- schemes: z.array(z.string()),
50
- credentials: z.string().nullable().default(null),
51
- })
52
- .nullable()
53
- .default(null);
54
-
55
- // Pricing plan schema
56
- export const PricingSchema = z.object({
57
- id: z.string(),
58
- description: z.string().nullable().optional(),
59
- currency: z.string(),
60
- amount: z
61
- .number()
62
- .describe(
63
- "Cost in standard units of currency (e.g., BSV, USD, not satoshis)",
64
- ),
65
- address: z.string(),
66
- acceptedCurrencies: z.array(z.string()).optional(),
67
- skills: z.array(z.string()).optional(),
68
- interval: z.enum(["day", "week", "month", "year"]).nullable().optional(),
69
- includedCalls: z.record(z.number()).optional(),
70
- });
71
-
72
- export type PricingConfig = z.infer<typeof PricingSchema>;
73
-
74
- export const AgentCardSchema = z.object({
75
- name: z.string(),
76
- description: z.string().nullable().default(null),
77
- url: z.string().url(),
78
- provider: AgentProviderSchema,
79
- version: z.string(),
80
- documentationUrl: z.string().url().nullable().default(null),
81
- capabilities: AgentCapabilitiesSchema,
82
- authentication: AgentAuthenticationSchema,
83
- defaultInputModes: z.array(z.string()).default(["text"]),
84
- defaultOutputModes: z.array(z.string()).default(["text"]),
85
- skills: z.array(AgentSkillSchema),
86
- "x-payment": z.array(PricingSchema).optional().default([]),
87
- });
88
-
89
- // Schema for on-chain agent publish parameters
90
- export const a2bPublishArgsSchema = z.object({
91
- agentUrl: z
92
- .string()
93
- .url()
94
- .describe("Agent base URL (e.g. https://example.com)"),
95
- agentName: z.string().describe("Human-friendly agent name"),
96
- description: z
97
- .string()
98
- .nullable()
99
- .optional()
100
- .describe("Optional agent description"),
101
- providerOrganization: z
102
- .string()
103
- .optional()
104
- .describe("Optional provider organization name"),
105
- providerUrl: z.string().url().optional().describe("Optional provider URL"),
106
- version: z.string().optional().describe("Optional agent version"),
107
- documentationUrl: z
108
- .string()
109
- .url()
110
- .nullable()
111
- .optional()
112
- .describe("Optional documentation URL"),
113
- streaming: z
114
- .boolean()
115
- .default(false)
116
- .describe("Supports SSE (tasks/sendSubscribe)"),
117
- pushNotifications: z
118
- .boolean()
119
- .default(false)
120
- .describe("Supports push notifications"),
121
- stateTransitionHistory: z
122
- .boolean()
123
- .default(false)
124
- .describe("Supports state transition history"),
125
- defaultInputModes: z
126
- .array(z.string())
127
- .default(["text"])
128
- .describe("Default input modes"),
129
- defaultOutputModes: z
130
- .array(z.string())
131
- .default(["text"])
132
- .describe("Default output modes"),
133
- skills: z
134
- .array(AgentSkillSchema)
135
- .optional()
136
- .default([])
137
- .describe("List of agent skills"),
138
- destinationAddress: z
139
- .string()
140
- .optional()
141
- .describe("Optional target address for inscription"),
142
- });
143
- export type A2bPublishArgs = z.infer<typeof a2bPublishArgsSchema>;
144
-
145
- /**
146
- * Registers the wallet_a2bPublish tool for publishing an agent record on-chain
147
- */
148
- export function registerA2bPublishAgentTool(server: McpServer, wallet: Wallet) {
149
- server.tool(
150
- "wallet_a2bPublish",
151
- "Publish an agent.json record on-chain via Ordinal inscription",
152
- { ...a2bPublishArgsSchema.shape },
153
- async ({ agentUrl, agentName, description, providerOrganization, providerUrl, version, documentationUrl, streaming, pushNotifications, stateTransitionHistory, defaultInputModes, defaultOutputModes, skills, destinationAddress }) => {
154
- try {
155
- const paymentPk = wallet.getPaymentKey();
156
- if (!paymentPk) throw new Error("No private key available");
157
- const { paymentUtxos } = await wallet.getUtxos();
158
- if (!paymentUtxos?.length)
159
- throw new Error("No payment UTXOs available to fund inscription");
160
-
161
- // TODO: Get the skills from actually running the MCP instead of trusting the agent args for skills
162
- const walletAddress = paymentPk.toAddress().toString();
163
-
164
- // Create pricing plans using the new schema
165
- const pricingConfig: PricingConfig[] = [
166
- // {
167
- // id: "subscription-premium",
168
- // description: "Premium subscription with all features",
169
- // currency: "USD",
170
- // amount: 10,
171
- // address: walletAddress,
172
- // interval: "month",
173
- // skills: ["wallet_a2bPublish", "wallet_a2bCall"],
174
- // },
175
- // {
176
- // id: "subscription-free",
177
- // description: "Free tier with limited features",
178
- // currency: "USD",
179
- // amount: 0,
180
- // address: walletAddress,
181
- // interval: "month",
182
- // skills: ["wallet_a2bCall"],
183
- // },
184
- {
185
- id: "pay-per-call-default",
186
- description: "Pay-per-call for publish operations",
187
- currency: "USD",
188
- amount: 1,
189
- address: walletAddress,
190
- skills: ["wallet_a2bPublish"],
191
- interval: null,
192
- },
193
- // {
194
- // id: "pay-per-call-royalty",
195
- // description: "Royalty payment for agent",
196
- // currency: "USD",
197
- // amount: 1,
198
- // address: "1JOExxxxxxxxxxxx",
199
- // skills: ["wallet_a2bPublish"],
200
- // interval: null,
201
- // }
202
- ];
203
-
204
- // Assemble AgentCard with defaults and user overrides
205
- const agentCard = {
206
- name: agentName,
207
- description: description ?? null,
208
- url: agentUrl,
209
- provider:
210
- providerOrganization && providerUrl
211
- ? {
212
- organization: providerOrganization,
213
- url: providerUrl,
214
- }
215
- : null,
216
- version: version ?? packageJson.version,
217
- documentationUrl: documentationUrl ?? null,
218
- capabilities: {
219
- streaming,
220
- pushNotifications,
221
- stateTransitionHistory,
222
- },
223
- authentication: null,
224
- defaultInputModes,
225
- defaultOutputModes,
226
- skills,
227
- "x-payment-config": pricingConfig,
228
- };
229
- // Validate compliance
230
- AgentCardSchema.parse(agentCard);
231
- const fileContent = JSON.stringify(agentCard, null, 2);
232
- // Base64 payload for inscription
233
- const dataB64 = toBase64(toArray(fileContent));
234
- const inscription: Inscription = {
235
- dataB64,
236
- contentType: "application/json",
237
- };
238
- // Destination for the ordinal
239
- const targetAddress = destinationAddress ?? walletAddress;
240
- const destinations: Destination[] = [
241
- { address: targetAddress, inscription },
242
- ];
243
- // Default MAP metadata: file path, content type, encoding
244
- const metaData: PreMAP = { app: "bsv-mcp", type: "a2b" };
245
-
246
- const createOrdinalsConfig: CreateOrdinalsConfig = {
247
- utxos: paymentUtxos,
248
- destinations,
249
- paymentPk,
250
- changeAddress: walletAddress,
251
- metaData,
252
- };
253
-
254
- const identityPk = process.env.IDENTITY_KEY_WIF
255
- ? PrivateKey.fromWif(process.env.IDENTITY_KEY_WIF)
256
- : undefined;
257
- if (identityPk) {
258
- createOrdinalsConfig.signer = {
259
- idKey: identityPk,
260
- } as LocalSigner;
261
- }
262
-
263
- // Inscribe the ordinal on-chain via js-1sat-ord
264
- const result = await createOrdinals(createOrdinalsConfig);
265
- const changeResult = result as ChangeResult;
266
-
267
- const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
268
- if (!disableBroadcasting) {
269
- const broadcaster = new V5Broadcaster();
270
- await changeResult.tx.broadcast(broadcaster);
271
-
272
- // Refresh UTXOs
273
- try {
274
- await wallet.refreshUtxos();
275
- } catch {}
276
- // Return transaction details
277
- return {
278
- content: [
279
- {
280
- type: "text",
281
- text: JSON.stringify({
282
- txid: changeResult.tx.id("hex"),
283
- spentOutpoints: changeResult.spentOutpoints,
284
- payChange: changeResult.payChange,
285
- inscriptionAddress: targetAddress,
286
- agentCard,
287
- }),
288
- },
289
- ],
290
- };
291
- }
292
-
293
- return {
294
- content: [
295
- {
296
- type: "text",
297
- text: changeResult.tx.toHex(),
298
- },
299
- ],
300
- };
301
- } catch (err: unknown) {
302
- const msg = err instanceof Error ? err.message : String(err);
303
- return { content: [{ type: "text", text: msg }], isError: true };
304
- }
305
- },
306
- );
307
- }