bsv-mcp 0.2.9 → 0.2.10
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.
- package/dist/index.js +91390 -27617
- package/index.ts +27 -0
- package/package.json +3 -1
- package/tools/index.ts +7 -2
- package/tools/wallet/brc100.ts +572 -0
- package/tools/wallet/createOrdinals.ts +62 -143
- package/tools/wallet/getBalance.ts +29 -25
- package/tools/wallet/purchaseListing.ts +89 -290
- package/tools/wallet/schemas.ts +7 -11
- package/tools/wallet/sendToAddress.ts +46 -47
- package/tools/wallet/tools.ts +12 -86
- package/tools/wallet/transferOrdToken.ts +137 -129
package/tools/wallet/tools.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { OneSatContext } from "@1sat/actions";
|
|
2
|
+
import type { PrivateKey } from "@bsv/sdk";
|
|
2
3
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
4
|
import { registerA2bPublishMcpTool } from "./a2bPublishMcp";
|
|
5
|
+
import { registerBrc100Tools } from "./brc100";
|
|
4
6
|
import { registerCreateOrdinalsTool } from "./createOrdinals";
|
|
5
7
|
import { registerGatherCollectionInfoTool } from "./gatherCollectionInfo";
|
|
6
8
|
import { registerGetAddressTool } from "./getAddress";
|
|
7
9
|
import { registerWalletGetBalanceTool } from "./getBalance";
|
|
8
|
-
import { registerGetPublicKeyTool } from "./getPublicKey";
|
|
9
10
|
import { registerMintCollectionTool } from "./mintCollection";
|
|
10
11
|
import { registerPurchaseListingTool } from "./purchaseListing";
|
|
11
12
|
import { registerRefreshUtxosTool } from "./refreshUtxos";
|
|
12
|
-
import { walletEncryptionArgsSchema } from "./schemas";
|
|
13
13
|
import { registerSendToAddressTool } from "./sendToAddress";
|
|
14
14
|
import { registerTransferOrdTokenTool } from "./transferOrdToken";
|
|
15
15
|
import type { Wallet } from "./wallet";
|
|
@@ -21,28 +21,29 @@ export function registerWalletTools(
|
|
|
21
21
|
disableBroadcasting: boolean;
|
|
22
22
|
enableA2bTools: boolean;
|
|
23
23
|
identityPk?: PrivateKey;
|
|
24
|
+
ctx?: OneSatContext;
|
|
24
25
|
},
|
|
25
26
|
): void {
|
|
26
27
|
// Register the wallet_sendToAddress tool
|
|
27
|
-
registerSendToAddressTool(server,
|
|
28
|
+
registerSendToAddressTool(server, config.ctx);
|
|
28
29
|
|
|
29
30
|
// Register the wallet_getAddress tool
|
|
30
31
|
registerGetAddressTool(server, wallet);
|
|
31
32
|
|
|
32
|
-
// Register the wallet_getPublicKey tool
|
|
33
|
-
registerGetPublicKeyTool(server, wallet);
|
|
34
|
-
|
|
35
33
|
// Register the wallet_purchaseListing tool
|
|
36
|
-
registerPurchaseListingTool(server,
|
|
34
|
+
registerPurchaseListingTool(server, config.ctx);
|
|
37
35
|
|
|
38
36
|
// Register the wallet_transferOrdToken tool
|
|
39
|
-
registerTransferOrdTokenTool(server,
|
|
37
|
+
registerTransferOrdTokenTool(server, config.ctx);
|
|
40
38
|
|
|
41
39
|
// Register the wallet_refreshUtxos tool
|
|
42
40
|
registerRefreshUtxosTool(server, wallet);
|
|
43
41
|
|
|
44
42
|
// Register the wallet_getBalance tool
|
|
45
|
-
registerWalletGetBalanceTool(server,
|
|
43
|
+
registerWalletGetBalanceTool(server, config.ctx);
|
|
44
|
+
|
|
45
|
+
// Register full BRC-100 wallet interface
|
|
46
|
+
registerBrc100Tools(server, config.ctx);
|
|
46
47
|
|
|
47
48
|
// A2B tools have to be explicitly enabled
|
|
48
49
|
if (config.enableA2bTools) {
|
|
@@ -52,83 +53,8 @@ export function registerWalletTools(
|
|
|
52
53
|
});
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
// Register combined wallet_encryption tool
|
|
56
|
-
server.tool(
|
|
57
|
-
"wallet_encryption",
|
|
58
|
-
"Combined tool for encrypting and decrypting data using the wallet's cryptographic keys.\n\n" +
|
|
59
|
-
"PARAMETERS:\n" +
|
|
60
|
-
'- mode: (required) Either "encrypt" to encrypt plaintext or "decrypt" to decrypt ciphertext\n' +
|
|
61
|
-
"- data: (required) Text string or array of numbers to process\n" +
|
|
62
|
-
"- encoding: (optional) For text input, the encoding format (utf8, hex, base64) - default is utf8\n" +
|
|
63
|
-
"- recipientPublicKeyHex: (optional) Hexadecimal representation of the recipient's public key (required for encrypt mode)\n\n" +
|
|
64
|
-
"EXAMPLES:\n" +
|
|
65
|
-
"1. Encrypt text data:\n" +
|
|
66
|
-
" {\n" +
|
|
67
|
-
' "mode": "encrypt",\n' +
|
|
68
|
-
' "data": "Hello World",\n' +
|
|
69
|
-
' "encoding": "utf8",\n' +
|
|
70
|
-
' "recipientPublicKeyHex": "020202020202020202020202020202020202020202020202020202020202020202"\n' +
|
|
71
|
-
" }\n\n" +
|
|
72
|
-
"2. Decrypt previously encrypted data:\n" +
|
|
73
|
-
" {\n" +
|
|
74
|
-
' "mode": "decrypt",\n' +
|
|
75
|
-
' "data": [encrypted bytes from previous response]\n' +
|
|
76
|
-
" }",
|
|
77
|
-
{ ...walletEncryptionArgsSchema.shape },
|
|
78
|
-
async ({ mode, data, encoding, recipientPublicKeyHex }) => {
|
|
79
|
-
try {
|
|
80
|
-
|
|
81
|
-
let inputData: number[];
|
|
82
|
-
if (typeof data === "string") {
|
|
83
|
-
inputData = Utils.toArray(data, encoding || "utf8");
|
|
84
|
-
} else {
|
|
85
|
-
inputData = Array.from(data);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// let resultBuffer: Buffer;
|
|
89
|
-
// let resultOutput: string | number[];
|
|
90
|
-
|
|
91
|
-
if (mode === "encrypt") {
|
|
92
|
-
// recipientPublicKeyHex is validated by Zod schema to be present for encrypt mode
|
|
93
|
-
// The schema ensures recipientPublicKeyHex is defined here.
|
|
94
|
-
// resultBuffer = await wallet.encrypt({
|
|
95
|
-
// data: inputBuffer,
|
|
96
|
-
// recipientPublicKeyHex: recipientPublicKeyHex as string, // Still needs type assertion if Zod optional isn't fully narrowed
|
|
97
|
-
// });
|
|
98
|
-
throw new Error("Encrypt method not yet implemented in wallet");
|
|
99
|
-
// if (encoding === "utf8") {
|
|
100
|
-
// resultOutput = Array.from(resultBuffer);
|
|
101
|
-
// } else {
|
|
102
|
-
// resultOutput = resultBuffer.toString(encoding || "base64");
|
|
103
|
-
// }
|
|
104
|
-
}
|
|
105
|
-
// mode === "decrypt"
|
|
106
|
-
// resultBuffer = await wallet.decrypt({ data: inputBuffer });
|
|
107
|
-
throw new Error("Decrypt method not yet implemented in wallet");
|
|
108
|
-
|
|
109
|
-
// return {
|
|
110
|
-
// content: [
|
|
111
|
-
// { type: "text", text: JSON.stringify({ data: resultOutput }) },
|
|
112
|
-
// ],
|
|
113
|
-
// };
|
|
114
|
-
} catch (error) {
|
|
115
|
-
const errorMessage =
|
|
116
|
-
error instanceof Error ? error.message : String(error);
|
|
117
|
-
return {
|
|
118
|
-
content: [
|
|
119
|
-
{
|
|
120
|
-
type: "text",
|
|
121
|
-
text: `Error during ${mode}: ${errorMessage}`,
|
|
122
|
-
},
|
|
123
|
-
],
|
|
124
|
-
isError: true,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
);
|
|
129
|
-
|
|
130
56
|
// Register createOrdinals tool
|
|
131
|
-
registerCreateOrdinalsTool(server,
|
|
57
|
+
registerCreateOrdinalsTool(server, config.ctx);
|
|
132
58
|
|
|
133
59
|
// Register collection tools
|
|
134
60
|
registerGatherCollectionInfoTool(server, wallet);
|
|
@@ -1,156 +1,164 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
51
|
+
ctx: OneSatContext | undefined,
|
|
59
52
|
) {
|
|
60
53
|
server.tool(
|
|
61
|
-
|
|
62
|
-
"
|
|
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 ({
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
if (result.error) {
|
|
103
|
+
return {
|
|
104
|
+
content: [{ type: 'text', text: result.error }],
|
|
105
|
+
isError: true,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
} as LocalSigner;
|
|
109
|
+
return {
|
|
110
|
+
content: [{ type: 'text', text: JSON.stringify({ txid: result.txid }) }],
|
|
111
|
+
}
|
|
113
112
|
}
|
|
114
113
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
134
|
+
const result = await sendBsv21.execute(ctx, {
|
|
135
|
+
tokenId,
|
|
136
|
+
amount,
|
|
137
|
+
address,
|
|
138
|
+
counterparty,
|
|
139
|
+
})
|
|
126
140
|
|
|
127
|
-
|
|
141
|
+
if (result.error) {
|
|
128
142
|
return {
|
|
129
|
-
content: [
|
|
130
|
-
|
|
131
|
-
|
|
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:
|
|
146
|
-
text:
|
|
155
|
+
type: 'text',
|
|
156
|
+
text: err instanceof Error ? err.message : String(err),
|
|
147
157
|
},
|
|
148
158
|
],
|
|
149
|
-
|
|
150
|
-
|
|
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
|
}
|