bsv-mcp 0.2.0 → 0.2.7
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/CHANGELOG.md +13 -0
- package/dist/index.js +87272 -70728
- package/index.ts +409 -22
- package/package.json +28 -24
- package/tools/a2b/discover.ts +13 -13
- package/tools/bap/friend.ts +2 -3
- package/tools/bap/generate.ts +7 -9
- package/tools/bap/getCurrentAddress.ts +1 -8
- package/tools/bap/getId.ts +3 -3
- package/tools/bsocial/bmapFollow.ts +3 -7
- package/tools/bsocial/bmapLikes.ts +3 -3
- package/tools/bsocial/bmapReadPosts.ts +3 -3
- package/tools/bsocial/createPost.ts +3 -3
- package/tools/bsocial/readPosts.ts +3 -3
- package/tools/bsv/decodeTransaction.ts +2 -5
- package/tools/bsv/explore.ts +2 -3
- package/tools/bsv/getPrice.ts +1 -9
- package/tools/bsv/token.ts +14 -26
- package/tools/index.ts +0 -13
- package/tools/mnee/getBalance.ts +2 -4
- package/tools/mnee/parseTx.ts +3 -3
- package/tools/mnee/sendMnee.ts +5 -5
- package/tools/ordinals/getInscription.ts +2 -3
- package/tools/ordinals/getTokenByIdOrTicker.ts +6 -3
- package/tools/ordinals/marketListings.ts +2 -18
- package/tools/ordinals/marketSales.ts +2 -4
- package/tools/ordinals/searchInscriptions.ts +2 -4
- package/tools/utils/index.ts +14 -16
- package/tools/wallet/a2bPublishAgent.ts +18 -27
- package/tools/wallet/a2bPublishMcp.ts +17 -22
- package/tools/wallet/createOrdinals.ts +11 -20
- package/tools/wallet/fetchPaymentUtxos.ts +9 -1
- package/tools/wallet/gatherCollectionInfo.ts +9 -13
- package/tools/wallet/getAddress.ts +1 -9
- package/tools/wallet/getBalance.ts +2 -14
- package/tools/wallet/getBalanceDroplet.ts +2 -13
- package/tools/wallet/getPublicKey.ts +3 -16
- package/tools/wallet/mintCollection.ts +17 -22
- package/tools/wallet/purchaseListing.ts +19 -29
- package/tools/wallet/refreshUtxos.ts +2 -14
- package/tools/wallet/sendOrdinals.ts +11 -20
- package/tools/wallet/sendToAddress.ts +2 -22
- package/tools/wallet/setupDroplet.ts +7 -18
- package/tools/wallet/tools.ts +10 -80
- package/tools/wallet/transferOrdToken.ts +12 -20
- package/vite.config.ts +15 -0
- package/tools/bigblocks/components.ts +0 -304
- package/tools/bigblocks/docs.ts +0 -488
- package/tools/bigblocks/examples.ts +0 -527
- package/tools/bigblocks/generator.ts +0 -485
- package/tools/bigblocks/index.ts +0 -23
- package/tools/bsocial/bigblocksApiClient.ts +0 -189
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type { PrivateKey
|
|
1
|
+
import type { PrivateKey } from "@bsv/sdk";
|
|
2
2
|
import { Utils } from "@bsv/sdk";
|
|
3
3
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
4
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5
5
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
-
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
7
6
|
import type {
|
|
8
7
|
ClientNotification,
|
|
9
8
|
ClientRequest,
|
|
@@ -19,10 +18,9 @@ import type {
|
|
|
19
18
|
PreMAP,
|
|
20
19
|
} from "js-1sat-ord";
|
|
21
20
|
import { createOrdinals } from "js-1sat-ord";
|
|
22
|
-
import { Sigma } from "sigma-protocol";
|
|
23
21
|
import { z } from "zod";
|
|
24
22
|
import packageJson from "../../package.json";
|
|
25
|
-
import {
|
|
23
|
+
import { V5Broadcaster } from "../../utils/broadcaster";
|
|
26
24
|
import type { Wallet } from "./wallet";
|
|
27
25
|
|
|
28
26
|
const { toArray, toBase64 } = Utils;
|
|
@@ -196,11 +194,8 @@ export function registerA2bPublishMcpTool(
|
|
|
196
194
|
server.tool(
|
|
197
195
|
"wallet_a2bPublishMcp",
|
|
198
196
|
"Publish an MCP tool configuration record on-chain via Ordinal inscription. This creates a permanent, immutable, and discoverable tool definition that can be accessed by other MCP servers. The tool is published as a JSON inscription with metadata and optional digital signatures for authenticity verification.",
|
|
199
|
-
{
|
|
200
|
-
async (
|
|
201
|
-
{ args }: { args: A2bPublishMcpArgs },
|
|
202
|
-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
203
|
-
) => {
|
|
197
|
+
{ ...a2bPublishMcpArgsSchema.shape },
|
|
198
|
+
async ({ toolName, command, args, keywords, env, description, destinationAddress }) => {
|
|
204
199
|
try {
|
|
205
200
|
if (!identityPk) {
|
|
206
201
|
console.warn(
|
|
@@ -218,20 +213,20 @@ export function registerA2bPublishMcpTool(
|
|
|
218
213
|
const walletAddress = paymentPk.toAddress().toString();
|
|
219
214
|
|
|
220
215
|
// Fetch MCP metadata (tools, prompts, resources)
|
|
221
|
-
const metadata = await fetchMcpMetadata(
|
|
216
|
+
const metadata = await fetchMcpMetadata(command, args);
|
|
222
217
|
// console.log(`Discovered ${metadata.tools.length} tools, ${metadata.prompts.length} prompts, and ${metadata.resources.length} resources`);
|
|
223
218
|
|
|
224
219
|
// Assemble tool configuration
|
|
225
220
|
const toolConfig: McpConfig = {
|
|
226
|
-
command
|
|
227
|
-
args
|
|
221
|
+
command,
|
|
222
|
+
args,
|
|
228
223
|
tools: metadata.tools,
|
|
229
224
|
prompts: metadata.prompts,
|
|
230
225
|
resources: metadata.resources,
|
|
231
|
-
env:
|
|
232
|
-
?
|
|
233
|
-
(acc, { key, description }) => {
|
|
234
|
-
acc[key] =
|
|
226
|
+
env: env
|
|
227
|
+
? env.reduce(
|
|
228
|
+
(acc, { key, description: desc }) => {
|
|
229
|
+
acc[key] = desc;
|
|
235
230
|
return acc;
|
|
236
231
|
},
|
|
237
232
|
{} as Record<string, string>,
|
|
@@ -245,9 +240,9 @@ export function registerA2bPublishMcpTool(
|
|
|
245
240
|
// Prepare the full configuration with metadata
|
|
246
241
|
const fullConfig = {
|
|
247
242
|
mcpServers: {
|
|
248
|
-
[
|
|
249
|
-
description:
|
|
250
|
-
keywords:
|
|
243
|
+
[toolName]: {
|
|
244
|
+
description: description || "",
|
|
245
|
+
keywords: keywords || [],
|
|
251
246
|
tools: metadata.tools || [],
|
|
252
247
|
prompts: metadata.prompts || [],
|
|
253
248
|
resources: metadata.resources || [],
|
|
@@ -266,7 +261,7 @@ export function registerA2bPublishMcpTool(
|
|
|
266
261
|
};
|
|
267
262
|
|
|
268
263
|
// Destination for the ordinal
|
|
269
|
-
const targetAddress =
|
|
264
|
+
const targetAddress = destinationAddress ?? walletAddress;
|
|
270
265
|
const destinations: Destination[] = [
|
|
271
266
|
{ address: targetAddress, inscription },
|
|
272
267
|
];
|
|
@@ -335,12 +330,12 @@ export function registerA2bPublishMcpTool(
|
|
|
335
330
|
txid,
|
|
336
331
|
outpoint,
|
|
337
332
|
onchainUrl,
|
|
338
|
-
toolName
|
|
333
|
+
toolName,
|
|
339
334
|
toolCount: metadata.tools.length,
|
|
340
335
|
promptCount: metadata.prompts.length,
|
|
341
336
|
resourceCount: metadata.resources.length,
|
|
342
337
|
description:
|
|
343
|
-
|
|
338
|
+
description || `MCP Tool: ${toolName}`,
|
|
344
339
|
address: targetAddress,
|
|
345
340
|
},
|
|
346
341
|
null,
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { PrivateKey } from "@bsv/sdk";
|
|
2
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
CallToolResult,
|
|
6
|
-
ServerNotification,
|
|
7
|
-
ServerRequest,
|
|
8
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
9
4
|
import type {
|
|
10
5
|
ChangeResult,
|
|
11
6
|
CreateOrdinalsCollectionItemMetadata,
|
|
@@ -16,10 +11,9 @@ import type {
|
|
|
16
11
|
LocalSigner,
|
|
17
12
|
PreMAP,
|
|
18
13
|
} from "js-1sat-ord";
|
|
19
|
-
import { createOrdinals
|
|
20
|
-
import { Sigma } from "sigma-protocol";
|
|
14
|
+
import { createOrdinals } from "js-1sat-ord";
|
|
21
15
|
import { z } from "zod";
|
|
22
|
-
import {
|
|
16
|
+
import { V5Broadcaster } from "../../utils/broadcaster";
|
|
23
17
|
import type { Wallet } from "./wallet";
|
|
24
18
|
|
|
25
19
|
/**
|
|
@@ -53,11 +47,8 @@ export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
|
|
|
53
47
|
server.tool(
|
|
54
48
|
"wallet_createOrdinals",
|
|
55
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.",
|
|
56
|
-
{
|
|
57
|
-
async (
|
|
58
|
-
{ args }: { args: CreateOrdinalsArgs },
|
|
59
|
-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
60
|
-
): Promise<CallToolResult> => {
|
|
50
|
+
{ ...createOrdinalsArgsSchema.shape },
|
|
51
|
+
async ({ dataB64, contentType, destinationAddress, metadata }): Promise<CallToolResult> => {
|
|
61
52
|
try {
|
|
62
53
|
// Load optional identity key for sigma signing
|
|
63
54
|
const identityKeyWif = process.env.IDENTITY_KEY_WIF;
|
|
@@ -92,14 +83,14 @@ export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
|
|
|
92
83
|
|
|
93
84
|
// 4. Create the inscription object
|
|
94
85
|
const inscription: Inscription = {
|
|
95
|
-
dataB64:
|
|
96
|
-
contentType:
|
|
86
|
+
dataB64: dataB64,
|
|
87
|
+
contentType: contentType,
|
|
97
88
|
};
|
|
98
89
|
|
|
99
90
|
// 5. Create the destination
|
|
100
91
|
const destinations: Destination[] = [
|
|
101
92
|
{
|
|
102
|
-
address:
|
|
93
|
+
address: destinationAddress || walletAddress,
|
|
103
94
|
inscription,
|
|
104
95
|
},
|
|
105
96
|
];
|
|
@@ -109,7 +100,7 @@ export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
|
|
|
109
100
|
destinations,
|
|
110
101
|
paymentPk,
|
|
111
102
|
changeAddress: walletAddress,
|
|
112
|
-
metaData:
|
|
103
|
+
metaData: metadata as
|
|
113
104
|
| PreMAP
|
|
114
105
|
| CreateOrdinalsCollectionMetadata
|
|
115
106
|
| CreateOrdinalsCollectionItemMetadata,
|
|
@@ -152,8 +143,8 @@ export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
|
|
|
152
143
|
txid: changeResult.tx.id("hex"),
|
|
153
144
|
spentOutpoints: changeResult.spentOutpoints,
|
|
154
145
|
payChange: changeResult.payChange,
|
|
155
|
-
inscriptionAddress:
|
|
156
|
-
contentType:
|
|
146
|
+
inscriptionAddress: destinationAddress || walletAddress,
|
|
147
|
+
contentType: contentType,
|
|
157
148
|
}),
|
|
158
149
|
},
|
|
159
150
|
],
|
|
@@ -104,7 +104,15 @@ export async function fetchPaymentUtxosFromV5(
|
|
|
104
104
|
const url = `${V5_API_URL}/own/${address}/utxos?refresh=true&txo=true&script=true&limit=250&tags=p2pkh`;
|
|
105
105
|
console.error(`Fetching UTXOs from V5: ${url}`);
|
|
106
106
|
const response = await fetch(url);
|
|
107
|
-
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
console.error(`V5 UTXO fetch failed: ${response.status} ${response.statusText}`);
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
const data = (await response.json()) as V5Utxo[] | null;
|
|
112
|
+
if (!Array.isArray(data)) {
|
|
113
|
+
console.error(`V5 UTXO response was not an array: ${typeof data}`);
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
108
116
|
const utxos: Utxo[] = [];
|
|
109
117
|
for (const utxo of data) {
|
|
110
118
|
if (!utxo.data.p2pkh) {
|
|
@@ -133,15 +133,11 @@ export function registerGatherCollectionInfoTool(
|
|
|
133
133
|
server.tool(
|
|
134
134
|
"wallet_gatherCollectionInfo",
|
|
135
135
|
"Analyzes a folder of images and gathers all necessary information for minting an ordinals collection. This includes validating images, checking wallet balance, estimating costs, and suggesting metadata. Use this before minting to ensure everything is ready.",
|
|
136
|
-
{
|
|
137
|
-
async ({
|
|
138
|
-
args,
|
|
139
|
-
}: {
|
|
140
|
-
args: GatherCollectionInfoArgs;
|
|
141
|
-
}): Promise<CallToolResult> => {
|
|
136
|
+
{ ...gatherCollectionInfoArgsSchema.shape },
|
|
137
|
+
async ({ folderPath }): Promise<CallToolResult> => {
|
|
142
138
|
try {
|
|
143
139
|
const analysis: CollectionAnalysis = {
|
|
144
|
-
folderPath
|
|
140
|
+
folderPath,
|
|
145
141
|
totalImages: 0,
|
|
146
142
|
validImages: 0,
|
|
147
143
|
invalidImages: 0,
|
|
@@ -172,13 +168,13 @@ export function registerGatherCollectionInfoTool(
|
|
|
172
168
|
|
|
173
169
|
// Check if folder exists
|
|
174
170
|
try {
|
|
175
|
-
const folderStat = await fs.stat(
|
|
171
|
+
const folderStat = await fs.stat(folderPath);
|
|
176
172
|
if (!folderStat.isDirectory()) {
|
|
177
|
-
throw new Error(`Path is not a directory: ${
|
|
173
|
+
throw new Error(`Path is not a directory: ${folderPath}`);
|
|
178
174
|
}
|
|
179
175
|
} catch (error) {
|
|
180
176
|
analysis.errors.push(
|
|
181
|
-
`Folder not found or inaccessible: ${
|
|
177
|
+
`Folder not found or inaccessible: ${folderPath}`,
|
|
182
178
|
);
|
|
183
179
|
return {
|
|
184
180
|
content: [
|
|
@@ -222,7 +218,7 @@ export function registerGatherCollectionInfoTool(
|
|
|
222
218
|
}
|
|
223
219
|
|
|
224
220
|
// Scan folder for images
|
|
225
|
-
const files = await fs.readdir(
|
|
221
|
+
const files = await fs.readdir(folderPath);
|
|
226
222
|
const imageExtensions = [
|
|
227
223
|
".jpg",
|
|
228
224
|
".jpeg",
|
|
@@ -233,7 +229,7 @@ export function registerGatherCollectionInfoTool(
|
|
|
233
229
|
];
|
|
234
230
|
|
|
235
231
|
for (const file of files) {
|
|
236
|
-
const filePath = path.join(
|
|
232
|
+
const filePath = path.join(folderPath, file);
|
|
237
233
|
const stat = await fs.stat(filePath);
|
|
238
234
|
|
|
239
235
|
if (stat.isFile()) {
|
|
@@ -280,7 +276,7 @@ export function registerGatherCollectionInfoTool(
|
|
|
280
276
|
}
|
|
281
277
|
|
|
282
278
|
// Suggest metadata based on folder name
|
|
283
|
-
const folderName = path.basename(
|
|
279
|
+
const folderName = path.basename(folderPath);
|
|
284
280
|
analysis.suggestedMetadata = {
|
|
285
281
|
collectionName: folderName
|
|
286
282
|
.replace(/[-_]/g, " ")
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { z } from "zod";
|
|
3
2
|
import type { Wallet } from "./wallet";
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -14,14 +13,7 @@ export function registerGetAddressTool(
|
|
|
14
13
|
server.tool(
|
|
15
14
|
"wallet_getAddress",
|
|
16
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.",
|
|
17
|
-
{
|
|
18
|
-
args: z
|
|
19
|
-
.object({})
|
|
20
|
-
.optional()
|
|
21
|
-
.describe(
|
|
22
|
-
"No parameters required - simply returns the current wallet address",
|
|
23
|
-
),
|
|
24
|
-
},
|
|
16
|
+
{},
|
|
25
17
|
async () => {
|
|
26
18
|
try {
|
|
27
19
|
const address = wallet.getAddress();
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
-
import type {
|
|
4
|
-
ServerNotification,
|
|
5
|
-
ServerRequest,
|
|
6
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
2
|
import { toBitcoin } from "satoshi-token"; // For converting satoshis to BSV string
|
|
8
|
-
import { z } from "zod";
|
|
9
3
|
import type { Wallet } from "./wallet";
|
|
10
4
|
|
|
11
|
-
export const walletGetBalanceArgsSchema = z.object({}).optional();
|
|
12
|
-
export type WalletGetBalanceArgs = z.infer<typeof walletGetBalanceArgsSchema>;
|
|
13
|
-
|
|
14
5
|
/**
|
|
15
6
|
* Registers the wallet_getBalance tool
|
|
16
7
|
*/
|
|
@@ -21,11 +12,8 @@ export function registerWalletGetBalanceTool(
|
|
|
21
12
|
server.tool(
|
|
22
13
|
"wallet_getBalance",
|
|
23
14
|
"Retrieves the current BSV balance for the wallet based on its UTXOs.",
|
|
24
|
-
{
|
|
25
|
-
async (
|
|
26
|
-
args: WalletGetBalanceArgs, // Args are empty but present for schema consistency
|
|
27
|
-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
28
|
-
) => {
|
|
15
|
+
{},
|
|
16
|
+
async () => {
|
|
29
17
|
try {
|
|
30
18
|
const { paymentUtxos } = await wallet.getUtxos();
|
|
31
19
|
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
-
import type {
|
|
4
|
-
ServerNotification,
|
|
5
|
-
ServerRequest,
|
|
6
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
2
|
import type { DropletClient } from "../../utils/droplet";
|
|
8
|
-
import { emptyArgsSchema } from "./schemas";
|
|
9
3
|
|
|
10
4
|
/**
|
|
11
5
|
* Register the getBalance tool for Droplet API mode
|
|
@@ -17,13 +11,8 @@ export function registerWalletGetBalanceDropletTool(
|
|
|
17
11
|
server.tool(
|
|
18
12
|
"wallet_getBalance",
|
|
19
13
|
"Gets the current balance of the wallet (Droplet mode)",
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
},
|
|
23
|
-
async (
|
|
24
|
-
{ args }: { args: unknown },
|
|
25
|
-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
26
|
-
) => {
|
|
14
|
+
{},
|
|
15
|
+
async () => {
|
|
27
16
|
try {
|
|
28
17
|
const status = await dropletClient.getFaucetStatus();
|
|
29
18
|
|
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
-
import type {
|
|
4
|
-
ServerNotification,
|
|
5
|
-
ServerRequest,
|
|
6
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
-
import type { z } from "zod";
|
|
8
|
-
import { getPublicKeyArgsSchema } from "./schemas";
|
|
9
2
|
import type { Wallet } from "./wallet";
|
|
10
3
|
|
|
11
|
-
// Use the schema imported from schemas.ts
|
|
12
|
-
export type GetPublicKeyArgs = z.infer<typeof getPublicKeyArgsSchema>;
|
|
13
|
-
|
|
14
4
|
/**
|
|
15
5
|
* Register the getPublicKey tool
|
|
16
6
|
*/
|
|
@@ -18,13 +8,10 @@ export function registerGetPublicKeyTool(server: McpServer, wallet: Wallet) {
|
|
|
18
8
|
server.tool(
|
|
19
9
|
"wallet_getPublicKey",
|
|
20
10
|
"Retrieves the current wallet's public key. This public key can be used for cryptographic operations like signature verification or encryption.",
|
|
21
|
-
{
|
|
22
|
-
async (
|
|
23
|
-
{ args }: { args: GetPublicKeyArgs },
|
|
24
|
-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
25
|
-
) => {
|
|
11
|
+
{},
|
|
12
|
+
async () => {
|
|
26
13
|
try {
|
|
27
|
-
const result = await wallet.getPublicKey(
|
|
14
|
+
const result = await wallet.getPublicKey({});
|
|
28
15
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
29
16
|
} catch (err: unknown) {
|
|
30
17
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -2,12 +2,7 @@ import { promises as fs } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { Utils } from "@bsv/sdk";
|
|
4
4
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
-
import type {
|
|
6
|
-
import type {
|
|
7
|
-
CallToolResult,
|
|
8
|
-
ServerNotification,
|
|
9
|
-
ServerRequest,
|
|
10
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
11
6
|
import type {
|
|
12
7
|
ChangeResult,
|
|
13
8
|
CollectionItemSubTypeData,
|
|
@@ -125,8 +120,8 @@ export function registerMintCollectionTool(server: McpServer, wallet: Wallet) {
|
|
|
125
120
|
server.tool(
|
|
126
121
|
"wallet_mintCollection",
|
|
127
122
|
"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.",
|
|
128
|
-
{
|
|
129
|
-
async ({
|
|
123
|
+
{ ...mintCollectionArgsSchema.shape },
|
|
124
|
+
async ({ folderPath, collectionName, description, rarityLabels, traits, skipBroadcast }): Promise<CallToolResult> => {
|
|
130
125
|
try {
|
|
131
126
|
// Get keys from wallet
|
|
132
127
|
const paymentPk = wallet.getPaymentKey();
|
|
@@ -136,7 +131,7 @@ export function registerMintCollectionTool(server: McpServer, wallet: Wallet) {
|
|
|
136
131
|
const identityPk = wallet.getIdentityKey();
|
|
137
132
|
|
|
138
133
|
// Get image files
|
|
139
|
-
const imageFiles = await getImageFiles(
|
|
134
|
+
const imageFiles = await getImageFiles(folderPath);
|
|
140
135
|
if (imageFiles.length === 0) {
|
|
141
136
|
throw new Error("No image files found in the specified folder");
|
|
142
137
|
}
|
|
@@ -145,14 +140,14 @@ export function registerMintCollectionTool(server: McpServer, wallet: Wallet) {
|
|
|
145
140
|
|
|
146
141
|
// Prepare collection metadata
|
|
147
142
|
const collectionSubTypeData: CollectionSubTypeData = {
|
|
148
|
-
description:
|
|
143
|
+
description: description,
|
|
149
144
|
quantity: imageFiles.length,
|
|
150
145
|
// Transform rarity labels to correct format
|
|
151
146
|
rarityLabels:
|
|
152
|
-
|
|
147
|
+
rarityLabels?.map((r) => ({ label: r.label })) || [],
|
|
153
148
|
// Transform traits - need to match the CollectionTraits type
|
|
154
|
-
traits:
|
|
155
|
-
? Object.entries(
|
|
149
|
+
traits: traits
|
|
150
|
+
? Object.entries(traits).reduce((acc, [key, values]) => {
|
|
156
151
|
acc[key] = {
|
|
157
152
|
values,
|
|
158
153
|
occurancePercentages: values.map(() =>
|
|
@@ -168,14 +163,14 @@ export function registerMintCollectionTool(server: McpServer, wallet: Wallet) {
|
|
|
168
163
|
app: "ord",
|
|
169
164
|
type: "ord",
|
|
170
165
|
subType: "collection",
|
|
171
|
-
name:
|
|
166
|
+
name: collectionName,
|
|
172
167
|
subTypeData: collectionSubTypeData,
|
|
173
168
|
};
|
|
174
169
|
|
|
175
170
|
// Create collection icon
|
|
176
171
|
const svgString = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
177
172
|
<rect width="100" height="100" fill="#f0f0f0"/>
|
|
178
|
-
<text x="50" y="55" text-anchor="middle" font-family="Arial" font-size="16">${
|
|
173
|
+
<text x="50" y="55" text-anchor="middle" font-family="Arial" font-size="16">${collectionName}</text>
|
|
179
174
|
</svg>`;
|
|
180
175
|
const collectionIconData = Utils.toBase64(
|
|
181
176
|
Utils.toArray(svgString, "utf8"),
|
|
@@ -217,7 +212,7 @@ export function registerMintCollectionTool(server: McpServer, wallet: Wallet) {
|
|
|
217
212
|
|
|
218
213
|
let collectionTxid = "";
|
|
219
214
|
const disableBroadcasting =
|
|
220
|
-
|
|
215
|
+
skipBroadcast || process.env.DISABLE_BROADCASTING === "true";
|
|
221
216
|
|
|
222
217
|
if (!disableBroadcasting) {
|
|
223
218
|
const broadcaster = new V5Broadcaster();
|
|
@@ -251,7 +246,7 @@ export function registerMintCollectionTool(server: McpServer, wallet: Wallet) {
|
|
|
251
246
|
);
|
|
252
247
|
|
|
253
248
|
try {
|
|
254
|
-
const itemTraits = generateItemTraits(i,
|
|
249
|
+
const itemTraits = generateItemTraits(i, traits);
|
|
255
250
|
|
|
256
251
|
const itemSubTypeData: CollectionItemSubTypeData = {
|
|
257
252
|
collectionId: collectionTxid,
|
|
@@ -260,13 +255,13 @@ export function registerMintCollectionTool(server: McpServer, wallet: Wallet) {
|
|
|
260
255
|
};
|
|
261
256
|
|
|
262
257
|
// Assign rarity if labels provided
|
|
263
|
-
if (
|
|
258
|
+
if (rarityLabels && rarityLabels.length > 0) {
|
|
264
259
|
const rarityIndex = Math.floor(
|
|
265
|
-
(i / imageFiles.length) *
|
|
260
|
+
(i / imageFiles.length) * rarityLabels.length,
|
|
266
261
|
);
|
|
267
262
|
const rarityItem =
|
|
268
|
-
|
|
269
|
-
Math.min(rarityIndex,
|
|
263
|
+
rarityLabels[
|
|
264
|
+
Math.min(rarityIndex, rarityLabels.length - 1)
|
|
270
265
|
];
|
|
271
266
|
if (rarityItem) {
|
|
272
267
|
itemSubTypeData.rarityLabel = [
|
|
@@ -352,7 +347,7 @@ export function registerMintCollectionTool(server: McpServer, wallet: Wallet) {
|
|
|
352
347
|
totalItems: imageFiles.length,
|
|
353
348
|
errors: results.errors,
|
|
354
349
|
totalCost: results.totalCost,
|
|
355
|
-
summary: `Collection "${
|
|
350
|
+
summary: `Collection "${collectionName}" created with ${results.itemTxids.length}/${imageFiles.length} items successfully minted.${results.errors.length > 0 ? ` ${results.errors.length} items failed.` : ""}`,
|
|
356
351
|
},
|
|
357
352
|
null,
|
|
358
353
|
2,
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { PrivateKey } from "@bsv/sdk";
|
|
2
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
CallToolResult,
|
|
6
|
-
ServerNotification,
|
|
7
|
-
ServerRequest,
|
|
8
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
9
4
|
import {
|
|
10
5
|
type ChangeResult,
|
|
11
6
|
type ExistingListing,
|
|
@@ -20,8 +15,6 @@ import {
|
|
|
20
15
|
type TokenUtxo,
|
|
21
16
|
type Utxo,
|
|
22
17
|
} from "js-1sat-ord";
|
|
23
|
-
import { Sigma } from "sigma-protocol";
|
|
24
|
-
import type { z } from "zod";
|
|
25
18
|
import {
|
|
26
19
|
MARKET_FEE_PERCENTAGE,
|
|
27
20
|
MARKET_WALLET_ADDRESS,
|
|
@@ -86,11 +79,8 @@ export function registerPurchaseListingTool(server: McpServer, wallet: Wallet) {
|
|
|
86
79
|
server.tool(
|
|
87
80
|
"wallet_purchaseListing",
|
|
88
81
|
"Purchases a listing from the Bitcoin SV ordinals marketplace. Supports both NFT purchases (with royalty payments to original creators) and BSV-20/BSV-21 token purchases. The tool handles all aspects of the transaction - from fetching listing details, calculating fees, creating and broadcasting the transaction.",
|
|
89
|
-
{
|
|
90
|
-
async (
|
|
91
|
-
{ args }: { args: z.infer<typeof purchaseListingArgsSchema> },
|
|
92
|
-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
93
|
-
): Promise<CallToolResult> => {
|
|
82
|
+
{ ...purchaseListingArgsSchema.shape },
|
|
83
|
+
async ({ listingOutpoint, ordAddress, listingType, tokenProtocol, tokenID, description }): Promise<CallToolResult> => {
|
|
94
84
|
try {
|
|
95
85
|
// Load optional identity key for sigma signing
|
|
96
86
|
const identityKeyWif = process.env.IDENTITY_KEY_WIF;
|
|
@@ -108,7 +98,7 @@ export function registerPurchaseListingTool(server: McpServer, wallet: Wallet) {
|
|
|
108
98
|
|
|
109
99
|
// Fetch the listing info directly from the API
|
|
110
100
|
const response = await fetch(
|
|
111
|
-
`https://ordinals.gorillapool.io/api/txos/${
|
|
101
|
+
`https://ordinals.gorillapool.io/api/txos/${listingOutpoint}?script=true`,
|
|
112
102
|
);
|
|
113
103
|
if (!response.ok) {
|
|
114
104
|
throw new Error(
|
|
@@ -138,7 +128,7 @@ export function registerPurchaseListingTool(server: McpServer, wallet: Wallet) {
|
|
|
138
128
|
}
|
|
139
129
|
|
|
140
130
|
// Parse the listing outpoint to get txid and vout
|
|
141
|
-
const [txid, voutStr] =
|
|
131
|
+
const [txid, voutStr] = listingOutpoint.split("_");
|
|
142
132
|
if (!txid) {
|
|
143
133
|
throw new Error("Invalid outpoint format. Expected txid_vout");
|
|
144
134
|
}
|
|
@@ -182,12 +172,12 @@ Please fund this wallet address with enough BSV to cover the purchase price
|
|
|
182
172
|
// Create the purchase transaction based on listing type
|
|
183
173
|
let transaction: ChangeResult;
|
|
184
174
|
|
|
185
|
-
if (
|
|
186
|
-
if (!
|
|
175
|
+
if (listingType === "token") {
|
|
176
|
+
if (!tokenProtocol) {
|
|
187
177
|
throw new Error("tokenProtocol is required for token listings");
|
|
188
178
|
}
|
|
189
179
|
|
|
190
|
-
if (!
|
|
180
|
+
if (!tokenID) {
|
|
191
181
|
throw new Error("tokenID is required for token listings");
|
|
192
182
|
}
|
|
193
183
|
|
|
@@ -203,7 +193,7 @@ Please fund this wallet address with enough BSV to cover the purchase price
|
|
|
203
193
|
|
|
204
194
|
// Convert the token protocol to the enum type expected by js-1sat-ord
|
|
205
195
|
const protocol =
|
|
206
|
-
|
|
196
|
+
tokenProtocol === "bsv-20" ? TokenType.BSV20 : TokenType.BSV21;
|
|
207
197
|
|
|
208
198
|
// Create a TokenUtxo with the required fields
|
|
209
199
|
const listingUtxo: TokenUtxo = {
|
|
@@ -212,17 +202,17 @@ Please fund this wallet address with enough BSV to cover the purchase price
|
|
|
212
202
|
script: listingData.script,
|
|
213
203
|
satoshis: 1, // TokenUtxo's satoshis must be exactly 1
|
|
214
204
|
amt: listingData.data.bsv20.amt,
|
|
215
|
-
id:
|
|
205
|
+
id: tokenID,
|
|
216
206
|
payout: listingData.data.list.payout,
|
|
217
207
|
};
|
|
218
208
|
|
|
219
209
|
transaction = await purchaseOrdTokenListing({
|
|
220
210
|
protocol,
|
|
221
|
-
tokenID:
|
|
211
|
+
tokenID: tokenID,
|
|
222
212
|
utxos: paymentUtxos,
|
|
223
213
|
paymentPk,
|
|
224
214
|
listingUtxo,
|
|
225
|
-
ordAddress:
|
|
215
|
+
ordAddress: ordAddress,
|
|
226
216
|
additionalPayments,
|
|
227
217
|
metaData,
|
|
228
218
|
});
|
|
@@ -257,7 +247,7 @@ Please fund this wallet address with enough BSV to cover the purchase price
|
|
|
257
247
|
const purchaseOrdListingConfig: PurchaseOrdListingConfig = {
|
|
258
248
|
utxos: paymentUtxos,
|
|
259
249
|
paymentPk,
|
|
260
|
-
ordAddress:
|
|
250
|
+
ordAddress: ordAddress,
|
|
261
251
|
listing,
|
|
262
252
|
additionalPayments,
|
|
263
253
|
metaData,
|
|
@@ -301,16 +291,16 @@ Please fund this wallet address with enough BSV to cover the purchase price
|
|
|
301
291
|
status: resultStatus,
|
|
302
292
|
message: resultMessage,
|
|
303
293
|
txid: transaction.tx.id("hex"),
|
|
304
|
-
listingOutpoint:
|
|
305
|
-
destinationAddress:
|
|
306
|
-
listingType:
|
|
307
|
-
tokenProtocol:
|
|
308
|
-
tokenID:
|
|
294
|
+
listingOutpoint: listingOutpoint,
|
|
295
|
+
destinationAddress: ordAddress,
|
|
296
|
+
listingType: listingType,
|
|
297
|
+
tokenProtocol: tokenID ? tokenProtocol : undefined,
|
|
298
|
+
tokenID: tokenID,
|
|
309
299
|
price: listingData.data.list.price,
|
|
310
300
|
marketFee,
|
|
311
301
|
marketFeeAddress: MARKET_WALLET_ADDRESS,
|
|
312
302
|
royaltiesPaid:
|
|
313
|
-
|
|
303
|
+
listingType === "nft" &&
|
|
314
304
|
listingData.origin?.data?.map?.royalties
|
|
315
305
|
? JSON.parse(listingData.origin.data.map.royalties)
|
|
316
306
|
: undefined,
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
-
import type {
|
|
4
|
-
ServerNotification,
|
|
5
|
-
ServerRequest,
|
|
6
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
2
|
import { toBitcoin } from "satoshi-token";
|
|
8
|
-
import { z } from "zod";
|
|
9
3
|
import type { Wallet } from "./wallet";
|
|
10
4
|
|
|
11
|
-
const refreshUtxosArgsSchema = z.object({});
|
|
12
|
-
type RefreshUtxosArgs = z.infer<typeof refreshUtxosArgsSchema>;
|
|
13
|
-
|
|
14
5
|
/**
|
|
15
6
|
* Registers the wallet_refreshUtxos tool that refreshes and returns the UTXOs for the wallet
|
|
16
7
|
*/
|
|
@@ -18,11 +9,8 @@ export function registerRefreshUtxosTool(server: McpServer, wallet: Wallet) {
|
|
|
18
9
|
server.tool(
|
|
19
10
|
"wallet_refreshUtxos",
|
|
20
11
|
"Refreshes and returns the wallet's UTXOs. This is useful for debugging UTXO issues, ensuring the wallet has the latest transaction outputs, and for verifying available funds before making transactions.",
|
|
21
|
-
{
|
|
22
|
-
async (
|
|
23
|
-
{ args }: { args: RefreshUtxosArgs },
|
|
24
|
-
_extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
25
|
-
) => {
|
|
12
|
+
{},
|
|
13
|
+
async () => {
|
|
26
14
|
try {
|
|
27
15
|
// Force refresh the UTXOs
|
|
28
16
|
await wallet.refreshUtxos();
|