bsv-mcp 0.0.32 → 0.0.34
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 +15 -0
- package/README.md +18 -7
- package/index.ts +9 -3
- package/package.json +2 -1
- package/smithery.yaml +67 -10
- package/tools/index.ts +2 -2
- package/tools/wallet/a2bPublishAgent.ts +45 -17
- package/tools/wallet/a2bPublishMcp.ts +94 -40
- package/tools/wallet/createOrdinals.ts +66 -23
- package/tools/wallet/purchaseListing.ts +69 -35
- package/tools/wallet/sendOrdinals.ts +54 -21
- package/tools/wallet/tools.ts +13 -4
- package/tools/wallet/transferOrdToken.ts +35 -12
- package/tools/wallet/wallet.ts +13 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# BSV MCP Server Changelog
|
|
2
2
|
|
|
3
|
+
## v0.0.34 - Transaction Broadcast Control
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
- Added `DISABLE_BROADCASTING` environment variable to control transaction broadcasting behavior
|
|
7
|
+
- When set to "true", transactions are created but not broadcast to the network
|
|
8
|
+
- Returns raw transaction hex instead of broadcasting, useful for testing and review
|
|
9
|
+
- Code cleanup and organization improvements
|
|
10
|
+
|
|
11
|
+
## v0.0.33 - Identity Key Sigma Signing
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
- Added optional `IDENTITY_KEY_WIF` environment variable for sigma-protocol signing.
|
|
15
|
+
- `wallet_createOrdinals`, and `wallet_purchaseListing` tools now support signing with an identity key.
|
|
16
|
+
- Updated `README.md` to document `IDENTITY_KEY_WIF` usage and JSON configuration examples.
|
|
17
|
+
|
|
3
18
|
## v0.0.32 - Reliability Improvements
|
|
4
19
|
|
|
5
20
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -18,8 +18,7 @@ This project is built using [Bun](https://bun.sh/), a fast JavaScript runtime an
|
|
|
18
18
|
|
|
19
19
|
**macOS (using Homebrew):**
|
|
20
20
|
```bash
|
|
21
|
-
brew
|
|
22
|
-
brew install bun
|
|
21
|
+
brew install oven-sh/bun/bun
|
|
23
22
|
```
|
|
24
23
|
|
|
25
24
|
**macOS/Linux/WSL (using installer script):**
|
|
@@ -38,7 +37,7 @@ This server implements the [Model Context Protocol](https://modelcontextprotocol
|
|
|
38
37
|
|
|
39
38
|

|
|
40
39
|
|
|
41
|
-
> **Note:** The PRIVATE_KEY_WIF environment variable is now optional. Without it, the server runs in limited mode with educational resources and non-wallet tools available. Wallet and MNEE token operations require a valid private key.
|
|
40
|
+
> **Note:** The `PRIVATE_KEY_WIF` environment variable is now optional. Without it, the server runs in limited mode with educational resources and non-wallet tools available. Wallet and MNEE token operations require a valid private key. You can also set the `IDENTITY_KEY_WIF` environment variable to enable sigma-protocol signing of ordinals inscriptions for authentication, curation, and web-of-trust.
|
|
42
41
|
|
|
43
42
|
### Cursor
|
|
44
43
|
|
|
@@ -58,14 +57,15 @@ To use the BSV MCP server with [Cursor](https://cursor.sh/):
|
|
|
58
57
|
"bsv-mcp@latest"
|
|
59
58
|
],
|
|
60
59
|
"env": {
|
|
61
|
-
"PRIVATE_KEY_WIF": "<your_private_key_wif>"
|
|
60
|
+
"PRIVATE_KEY_WIF": "<your_private_key_wif>",
|
|
61
|
+
"IDENTITY_KEY_WIF": "<your_identity_key_wif>"
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
5. Replace `<your_private_key_wif>` with your actual private key WIF (keep this secure!) If you dont have one you can leave this off for now but you wont be able to use tools that require a wallet.
|
|
68
|
+
5. Replace `<your_private_key_wif>` with your actual private key WIF (keep this secure!) If you dont have one you can leave this off for now but you wont be able to use tools that require a wallet. `<your_identity_key_wif>` is also optional. It will sign 1Sat Ordinals with Sigma protocol using the provided identity key.
|
|
69
69
|
|
|
70
70
|
6. Click "Save"
|
|
71
71
|
|
|
@@ -84,7 +84,8 @@ If you prefer to use npm instead of Bun:
|
|
|
84
84
|
"bsv-mcp@latest"
|
|
85
85
|
],
|
|
86
86
|
"env": {
|
|
87
|
-
"PRIVATE_KEY_WIF": "<your_private_key_wif>"
|
|
87
|
+
"PRIVATE_KEY_WIF": "<your_private_key_wif>",
|
|
88
|
+
"IDENTITY_KEY_WIF": "<your_identity_key_wif>"
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
}
|
|
@@ -118,7 +119,8 @@ Open the Claude configuration json file in your favorite text editor. If you pre
|
|
|
118
119
|
"run", "bsv-mcp@latest"
|
|
119
120
|
],
|
|
120
121
|
"env": {
|
|
121
|
-
"PRIVATE_KEY_WIF": "<your_private_key_wif>"
|
|
122
|
+
"PRIVATE_KEY_WIF": "<your_private_key_wif>",
|
|
123
|
+
"IDENTITY_KEY_WIF": "<your_identity_key_wif>"
|
|
122
124
|
}
|
|
123
125
|
}
|
|
124
126
|
}
|
|
@@ -362,6 +364,8 @@ The BSV MCP server can be customized using environment variables to enable or di
|
|
|
362
364
|
| `DISABLE_BSV_TOOLS` | `false` | Set to `true` to disable BSV blockchain tools |
|
|
363
365
|
| `DISABLE_ORDINALS_TOOLS` | `false` | Set to `true` to disable Ordinals/NFT tools |
|
|
364
366
|
| `DISABLE_UTILS_TOOLS` | `false` | Set to `true` to disable utility tools |
|
|
367
|
+
| `IDENTITY_KEY_WIF` | `not set` | Optional WIF for identity key; if set, ordinals inscriptions will be signed with sigma-protocol for authentication, curation, and web-of-trust. |
|
|
368
|
+
| `DISABLE_BROADCASTING` | `false` | Set to `true` to disable transaction broadcasting; returns raw transaction hex instead - useful for testing and transaction review before broadcasting |
|
|
365
369
|
|
|
366
370
|
### Examples
|
|
367
371
|
|
|
@@ -383,6 +387,12 @@ Use all tools except wallet operations:
|
|
|
383
387
|
DISABLE_WALLET_TOOLS=true bunx bsv-mcp@latest
|
|
384
388
|
```
|
|
385
389
|
|
|
390
|
+
Create transactions without broadcasting them (dry-run mode):
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
DISABLE_BROADCASTING=true bunx bsv-mcp@latest
|
|
394
|
+
```
|
|
395
|
+
|
|
386
396
|
## Troubleshooting
|
|
387
397
|
|
|
388
398
|
If you're having issues with the BSV MCP server:
|
|
@@ -424,6 +434,7 @@ For Cursor, check the Cursor MCP logs in Settings → Extensions → Model Conte
|
|
|
424
434
|
|
|
425
435
|
## Recent Updates
|
|
426
436
|
|
|
437
|
+
- **Transaction Broadcast Control**: Added `DISABLE_BROADCASTING` environment variable to prevent transactions from being broadcast to the network
|
|
427
438
|
- **Blockchain Explorer**: Added `bsv_explore` tool for WhatsOnChain API access with mainnet/testnet support
|
|
428
439
|
- **Unified Tools**: Merged `wallet_encrypt`/`wallet_decrypt` into single `wallet_encryption` tool
|
|
429
440
|
- **Enhanced Marketplace**: Support for NFTs, BSV-20/21 tokens in listings, sales and purchases
|
package/index.ts
CHANGED
|
@@ -24,7 +24,10 @@ const CONFIG = {
|
|
|
24
24
|
loadBsvTools: process.env.DISABLE_BSV_TOOLS !== "true",
|
|
25
25
|
loadOrdinalsTools: process.env.DISABLE_ORDINALS_TOOLS !== "true",
|
|
26
26
|
loadUtilsTools: process.env.DISABLE_UTILS_TOOLS !== "true",
|
|
27
|
-
loadA2bTools: process.env.
|
|
27
|
+
loadA2bTools: process.env.ENABLE_A2B_TOOLS === "true",
|
|
28
|
+
|
|
29
|
+
// Transaction broadcasting control
|
|
30
|
+
disableBroadcasting: process.env.DISABLE_BROADCASTING === "true",
|
|
28
31
|
};
|
|
29
32
|
|
|
30
33
|
/**
|
|
@@ -70,7 +73,7 @@ function initializePrivateKey(): PrivateKey | undefined {
|
|
|
70
73
|
const privKey = initializePrivateKey();
|
|
71
74
|
|
|
72
75
|
const server = new McpServer(
|
|
73
|
-
{ name: "Bitcoin SV", version: "0.0.
|
|
76
|
+
{ name: "Bitcoin SV", version: "0.0.34" },
|
|
74
77
|
// {
|
|
75
78
|
// // Advertise only what you actually implement
|
|
76
79
|
// capabilities: {
|
|
@@ -102,7 +105,10 @@ if (CONFIG.loadTools) {
|
|
|
102
105
|
// Initialize wallet with the private key if wallet tools are enabled
|
|
103
106
|
if (CONFIG.loadWalletTools) {
|
|
104
107
|
wallet = new Wallet(privKey);
|
|
105
|
-
registerWalletTools(server, wallet
|
|
108
|
+
registerWalletTools(server, wallet, {
|
|
109
|
+
disableBroadcasting: CONFIG.disableBroadcasting,
|
|
110
|
+
enableA2bTools: CONFIG.loadA2bTools,
|
|
111
|
+
});
|
|
106
112
|
}
|
|
107
113
|
}
|
|
108
114
|
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "bsv-mcp",
|
|
3
3
|
"module": "index.ts",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.34",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "satchmo",
|
|
8
8
|
"description": "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"js-1sat-ord": "^0.1.81",
|
|
53
53
|
"mnee": "^2.0.0",
|
|
54
54
|
"satoshi-token": "^0.0.4",
|
|
55
|
+
"sigma-protocol": "^0.1.6",
|
|
55
56
|
"zod": "^3.24.3"
|
|
56
57
|
},
|
|
57
58
|
"scripts": {
|
package/smithery.yaml
CHANGED
|
@@ -5,17 +5,74 @@ startCommand:
|
|
|
5
5
|
type: stdio
|
|
6
6
|
configSchema:
|
|
7
7
|
type: object
|
|
8
|
-
required:
|
|
9
|
-
- privateKeyWif
|
|
10
8
|
properties:
|
|
11
9
|
privateKeyWif:
|
|
12
10
|
type: string
|
|
13
|
-
|
|
11
|
+
title: "Private Key (WIF)"
|
|
12
|
+
description: "The private key WIF (Wallet Import Format) for Bitcoin SV transactions. Optional but required for wallet operations. Without this, the server runs in limited mode with only educational resources and non-wallet tools."
|
|
13
|
+
disablePrompts:
|
|
14
|
+
type: boolean
|
|
15
|
+
title: "Disable Prompts"
|
|
16
|
+
description: "Set to true to disable all educational prompts"
|
|
17
|
+
default: false
|
|
18
|
+
disableResources:
|
|
19
|
+
type: boolean
|
|
20
|
+
title: "Disable Resources"
|
|
21
|
+
description: "Set to true to disable all resources (BRCs, changelog)"
|
|
22
|
+
default: false
|
|
23
|
+
disableTools:
|
|
24
|
+
type: boolean
|
|
25
|
+
title: "Disable All Tools"
|
|
26
|
+
description: "Set to true to disable all tools"
|
|
27
|
+
default: false
|
|
28
|
+
disableWalletTools:
|
|
29
|
+
type: boolean
|
|
30
|
+
title: "Disable Wallet Tools"
|
|
31
|
+
description: "Set to true to disable Bitcoin wallet tools"
|
|
32
|
+
default: false
|
|
33
|
+
disableMneeTools:
|
|
34
|
+
type: boolean
|
|
35
|
+
title: "Disable MNEE Tools"
|
|
36
|
+
description: "Set to true to disable MNEE token tools"
|
|
37
|
+
default: false
|
|
38
|
+
disableBsvTools:
|
|
39
|
+
type: boolean
|
|
40
|
+
title: "Disable BSV Blockchain Tools"
|
|
41
|
+
description: "Set to true to disable BSV blockchain tools"
|
|
42
|
+
default: false
|
|
43
|
+
disableOrdinalsTools:
|
|
44
|
+
type: boolean
|
|
45
|
+
title: "Disable Ordinals Tools"
|
|
46
|
+
description: "Set to true to disable Ordinals/NFT tools"
|
|
47
|
+
default: false
|
|
48
|
+
disableUtilsTools:
|
|
49
|
+
type: boolean
|
|
50
|
+
title: "Disable Utility Tools"
|
|
51
|
+
description: "Set to true to disable utility tools"
|
|
52
|
+
default: false
|
|
53
|
+
additionalProperties: false
|
|
14
54
|
commandFunction: |
|
|
15
|
-
(config) =>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
55
|
+
(config) => {
|
|
56
|
+
const env = {};
|
|
57
|
+
|
|
58
|
+
// Add private key if provided
|
|
59
|
+
if (config.privateKeyWif) {
|
|
60
|
+
env.PRIVATE_KEY_WIF = config.privateKeyWif;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Map boolean config options to environment variables
|
|
64
|
+
if (config.disablePrompts) env.DISABLE_PROMPTS = 'true';
|
|
65
|
+
if (config.disableResources) env.DISABLE_RESOURCES = 'true';
|
|
66
|
+
if (config.disableTools) env.DISABLE_TOOLS = 'true';
|
|
67
|
+
if (config.disableWalletTools) env.DISABLE_WALLET_TOOLS = 'true';
|
|
68
|
+
if (config.disableMneeTools) env.DISABLE_MNEE_TOOLS = 'true';
|
|
69
|
+
if (config.disableBsvTools) env.DISABLE_BSV_TOOLS = 'true';
|
|
70
|
+
if (config.disableOrdinalsTools) env.DISABLE_ORDINALS_TOOLS = 'true';
|
|
71
|
+
if (config.disableUtilsTools) env.DISABLE_UTILS_TOOLS = 'true';
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
command: 'bun',
|
|
75
|
+
args: ['run', 'index.ts'],
|
|
76
|
+
env
|
|
77
|
+
};
|
|
78
|
+
}
|
package/tools/index.ts
CHANGED
|
@@ -32,14 +32,14 @@ export function registerAllTools(
|
|
|
32
32
|
enableBsvTools: true,
|
|
33
33
|
enableOrdinalsTools: true,
|
|
34
34
|
enableUtilsTools: true,
|
|
35
|
-
enableA2bTools:
|
|
35
|
+
enableA2bTools: false,
|
|
36
36
|
},
|
|
37
37
|
): void {
|
|
38
38
|
const {
|
|
39
39
|
enableBsvTools = true,
|
|
40
40
|
enableOrdinalsTools = true,
|
|
41
41
|
enableUtilsTools = true,
|
|
42
|
-
enableA2bTools =
|
|
42
|
+
enableA2bTools = false,
|
|
43
43
|
} = config;
|
|
44
44
|
|
|
45
45
|
// Register BSV-related tools
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Utils } from "@bsv/sdk";
|
|
1
|
+
import { PrivateKey, Utils } from "@bsv/sdk";
|
|
2
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
4
4
|
import type {
|
|
@@ -8,10 +8,13 @@ import type {
|
|
|
8
8
|
import { createOrdinals } from "js-1sat-ord";
|
|
9
9
|
import type {
|
|
10
10
|
ChangeResult,
|
|
11
|
+
CreateOrdinalsConfig,
|
|
11
12
|
Destination,
|
|
12
13
|
Inscription,
|
|
14
|
+
LocalSigner,
|
|
13
15
|
PreMAP,
|
|
14
16
|
} from "js-1sat-ord";
|
|
17
|
+
import { Sigma } from "sigma-protocol";
|
|
15
18
|
import { z } from "zod";
|
|
16
19
|
import type { Wallet } from "./wallet";
|
|
17
20
|
const { toArray, toBase64 } = Utils;
|
|
@@ -246,32 +249,57 @@ export function registerA2bPublishAgentTool(server: McpServer, wallet: Wallet) {
|
|
|
246
249
|
// Default MAP metadata: file path, content type, encoding
|
|
247
250
|
const metaData: PreMAP = { app: "bsv-mcp", type: "a2b" };
|
|
248
251
|
|
|
249
|
-
|
|
250
|
-
const result = await createOrdinals({
|
|
252
|
+
const createOrdinalsConfig: CreateOrdinalsConfig = {
|
|
251
253
|
utxos: paymentUtxos,
|
|
252
254
|
destinations,
|
|
253
255
|
paymentPk,
|
|
254
256
|
changeAddress: walletAddress,
|
|
255
257
|
metaData,
|
|
256
|
-
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const identityPk = process.env.IDENTITY_KEY_WIF
|
|
261
|
+
? PrivateKey.fromWif(process.env.IDENTITY_KEY_WIF)
|
|
262
|
+
: undefined;
|
|
263
|
+
if (identityPk) {
|
|
264
|
+
createOrdinalsConfig.signer = {
|
|
265
|
+
idKey: identityPk,
|
|
266
|
+
} as LocalSigner;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Inscribe the ordinal on-chain via js-1sat-ord
|
|
270
|
+
const result = await createOrdinals(createOrdinalsConfig);
|
|
257
271
|
const changeResult = result as ChangeResult;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
await
|
|
262
|
-
|
|
263
|
-
|
|
272
|
+
|
|
273
|
+
const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
|
|
274
|
+
if (!disableBroadcasting) {
|
|
275
|
+
await changeResult.tx.broadcast();
|
|
276
|
+
|
|
277
|
+
// Refresh UTXOs
|
|
278
|
+
try {
|
|
279
|
+
await wallet.refreshUtxos();
|
|
280
|
+
} catch {}
|
|
281
|
+
// Return transaction details
|
|
282
|
+
return {
|
|
283
|
+
content: [
|
|
284
|
+
{
|
|
285
|
+
type: "text",
|
|
286
|
+
text: JSON.stringify({
|
|
287
|
+
txid: changeResult.tx.id("hex"),
|
|
288
|
+
spentOutpoints: changeResult.spentOutpoints,
|
|
289
|
+
payChange: changeResult.payChange,
|
|
290
|
+
inscriptionAddress: targetAddress,
|
|
291
|
+
agentCard,
|
|
292
|
+
}),
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
264
298
|
return {
|
|
265
299
|
content: [
|
|
266
300
|
{
|
|
267
301
|
type: "text",
|
|
268
|
-
text:
|
|
269
|
-
txid: changeResult.tx.id("hex"),
|
|
270
|
-
spentOutpoints: changeResult.spentOutpoints,
|
|
271
|
-
payChange: changeResult.payChange,
|
|
272
|
-
inscriptionAddress: targetAddress,
|
|
273
|
-
agentCard,
|
|
274
|
-
}),
|
|
302
|
+
text: changeResult.tx.toHex(),
|
|
275
303
|
},
|
|
276
304
|
],
|
|
277
305
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Utils } from "@bsv/sdk";
|
|
1
|
+
import { PrivateKey, Utils } from "@bsv/sdk";
|
|
2
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
4
4
|
import type {
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
Inscription,
|
|
13
13
|
PreMAP,
|
|
14
14
|
} from "js-1sat-ord";
|
|
15
|
+
import { Sigma } from "sigma-protocol";
|
|
15
16
|
import { z } from "zod";
|
|
16
17
|
import type { Wallet } from "./wallet";
|
|
17
18
|
const { toArray, toBase64 } = Utils;
|
|
@@ -31,9 +32,16 @@ export const a2bPublishMcpArgsSchema = z.object({
|
|
|
31
32
|
command: z.string().describe("The command to execute the tool"),
|
|
32
33
|
args: z.array(z.string()).describe("Arguments to pass to the command"),
|
|
33
34
|
env: z
|
|
34
|
-
.
|
|
35
|
+
.array(
|
|
36
|
+
z.object({
|
|
37
|
+
key: z.string().describe("Environment variable name"),
|
|
38
|
+
description: z
|
|
39
|
+
.string()
|
|
40
|
+
.describe("Description of the environment variable"),
|
|
41
|
+
}),
|
|
42
|
+
)
|
|
35
43
|
.optional()
|
|
36
|
-
.describe("Optional environment variables"),
|
|
44
|
+
.describe("Optional environment variables with descriptions"),
|
|
37
45
|
description: z.string().optional().describe("Optional tool description"),
|
|
38
46
|
destinationAddress: z
|
|
39
47
|
.string()
|
|
@@ -46,7 +54,11 @@ export type A2bPublishMcpArgs = z.infer<typeof a2bPublishMcpArgsSchema>;
|
|
|
46
54
|
/**
|
|
47
55
|
* Registers the wallet_a2bPublishMcp for publishing an MCP tool configuration on-chain
|
|
48
56
|
*/
|
|
49
|
-
export function registerA2bPublishMcpTool(
|
|
57
|
+
export function registerA2bPublishMcpTool(
|
|
58
|
+
server: McpServer,
|
|
59
|
+
wallet: Wallet,
|
|
60
|
+
config: { disableBroadcasting: boolean },
|
|
61
|
+
) {
|
|
50
62
|
server.tool(
|
|
51
63
|
"wallet_a2bPublishMcp",
|
|
52
64
|
"Publish an MCP tool configuration record on-chain via Ordinal inscription",
|
|
@@ -56,6 +68,20 @@ export function registerA2bPublishMcpTool(server: McpServer, wallet: Wallet) {
|
|
|
56
68
|
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
57
69
|
) => {
|
|
58
70
|
try {
|
|
71
|
+
// Load optional identity key for sigma signing
|
|
72
|
+
const identityKeyWif = process.env.IDENTITY_KEY_WIF;
|
|
73
|
+
let identityPk: PrivateKey | undefined;
|
|
74
|
+
if (identityKeyWif) {
|
|
75
|
+
try {
|
|
76
|
+
identityPk = PrivateKey.fromWif(identityKeyWif);
|
|
77
|
+
} catch (e) {
|
|
78
|
+
console.warn(
|
|
79
|
+
"Warning: Invalid IDENTITY_KEY_WIF environment variable; sigma signing disabled",
|
|
80
|
+
e,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
59
85
|
const paymentPk = wallet.getPrivateKey();
|
|
60
86
|
if (!paymentPk) throw new Error("No private key available");
|
|
61
87
|
|
|
@@ -69,7 +95,15 @@ export function registerA2bPublishMcpTool(server: McpServer, wallet: Wallet) {
|
|
|
69
95
|
const toolConfig: McpConfig = {
|
|
70
96
|
command: args.command,
|
|
71
97
|
args: args.args,
|
|
72
|
-
env: args.env
|
|
98
|
+
env: args.env
|
|
99
|
+
? args.env.reduce(
|
|
100
|
+
(acc, { key, description }) => {
|
|
101
|
+
acc[key] = description;
|
|
102
|
+
return acc;
|
|
103
|
+
},
|
|
104
|
+
{} as Record<string, string>,
|
|
105
|
+
)
|
|
106
|
+
: undefined,
|
|
73
107
|
};
|
|
74
108
|
|
|
75
109
|
// Validate compliance
|
|
@@ -77,10 +111,13 @@ export function registerA2bPublishMcpTool(server: McpServer, wallet: Wallet) {
|
|
|
77
111
|
|
|
78
112
|
// Prepare the full configuration with metadata
|
|
79
113
|
const fullConfig = {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
114
|
+
mcpServers: {
|
|
115
|
+
[args.toolName]: {
|
|
116
|
+
description: args.description || "",
|
|
117
|
+
type: "mcp-tool",
|
|
118
|
+
...toolConfig,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
84
121
|
};
|
|
85
122
|
|
|
86
123
|
const fileContent = JSON.stringify(fullConfig, null, 2);
|
|
@@ -112,43 +149,60 @@ export function registerA2bPublishMcpTool(server: McpServer, wallet: Wallet) {
|
|
|
112
149
|
|
|
113
150
|
const changeResult = result as ChangeResult;
|
|
114
151
|
|
|
152
|
+
let finalTx = changeResult.tx;
|
|
153
|
+
if (identityPk) {
|
|
154
|
+
const sigma = new Sigma(result.tx);
|
|
155
|
+
const signResponse = sigma.sign(identityPk);
|
|
156
|
+
finalTx = signResponse.signedTx;
|
|
157
|
+
}
|
|
115
158
|
// Broadcast the transaction
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
159
|
+
if (!config.disableBroadcasting) {
|
|
160
|
+
await finalTx.broadcast();
|
|
161
|
+
|
|
162
|
+
// Refresh UTXOs after spending
|
|
163
|
+
try {
|
|
164
|
+
await wallet.refreshUtxos();
|
|
165
|
+
} catch (refreshError) {
|
|
166
|
+
console.warn(
|
|
167
|
+
"Failed to refresh UTXOs after transaction:",
|
|
168
|
+
refreshError,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Build a nicely formatted result
|
|
173
|
+
const outpointIndex = 0; // First output with the inscription
|
|
174
|
+
const outpoint = `${finalTx.id("hex")}_${outpointIndex}`;
|
|
175
|
+
|
|
176
|
+
// Tool URL for discovery is the outpoint
|
|
177
|
+
const onchainUrl = `ord://${outpoint}`;
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
content: [
|
|
181
|
+
{
|
|
182
|
+
type: "text",
|
|
183
|
+
text: JSON.stringify(
|
|
184
|
+
{
|
|
185
|
+
status: "success",
|
|
186
|
+
txid: finalTx.id("hex"),
|
|
187
|
+
outpoint,
|
|
188
|
+
onchainUrl,
|
|
189
|
+
toolName: args.toolName,
|
|
190
|
+
description:
|
|
191
|
+
args.description || `MCP Tool: ${args.toolName}`,
|
|
192
|
+
address: targetAddress,
|
|
193
|
+
},
|
|
194
|
+
null,
|
|
195
|
+
2,
|
|
196
|
+
),
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
};
|
|
126
200
|
}
|
|
127
|
-
|
|
128
|
-
// Build a nicely formatted result
|
|
129
|
-
const outpointIndex = 0; // First output with the inscription
|
|
130
|
-
const outpoint = `${changeResult.tx.id("hex")}_${outpointIndex}`;
|
|
131
|
-
|
|
132
|
-
// Tool URL for discovery is the outpoint
|
|
133
|
-
const onchainUrl = `ord://${outpoint}`;
|
|
134
|
-
|
|
135
201
|
return {
|
|
136
202
|
content: [
|
|
137
203
|
{
|
|
138
204
|
type: "text",
|
|
139
|
-
text:
|
|
140
|
-
{
|
|
141
|
-
status: "success",
|
|
142
|
-
txid: changeResult.tx.id("hex"),
|
|
143
|
-
outpoint,
|
|
144
|
-
onchainUrl,
|
|
145
|
-
toolName: args.toolName,
|
|
146
|
-
description: args.description || `MCP Tool: ${args.toolName}`,
|
|
147
|
-
address: targetAddress,
|
|
148
|
-
},
|
|
149
|
-
null,
|
|
150
|
-
2,
|
|
151
|
-
),
|
|
205
|
+
text: finalTx.toHex(),
|
|
152
206
|
},
|
|
153
207
|
],
|
|
154
208
|
};
|
|
@@ -1,15 +1,23 @@
|
|
|
1
|
+
import { PrivateKey } from "@bsv/sdk";
|
|
1
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
3
|
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
CallToolResult,
|
|
6
|
+
ServerNotification,
|
|
7
|
+
ServerRequest,
|
|
8
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
4
9
|
import { createOrdinals } from "js-1sat-ord";
|
|
5
10
|
import type {
|
|
6
11
|
ChangeResult,
|
|
7
12
|
CreateOrdinalsCollectionItemMetadata,
|
|
8
13
|
CreateOrdinalsCollectionMetadata,
|
|
14
|
+
CreateOrdinalsConfig,
|
|
9
15
|
Destination,
|
|
10
16
|
Inscription,
|
|
17
|
+
LocalSigner,
|
|
11
18
|
PreMAP,
|
|
12
19
|
} from "js-1sat-ord";
|
|
20
|
+
import { Sigma } from "sigma-protocol";
|
|
13
21
|
import { z } from "zod";
|
|
14
22
|
import type { Wallet } from "./wallet";
|
|
15
23
|
|
|
@@ -47,9 +55,23 @@ export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
|
|
|
47
55
|
{ args: createOrdinalsArgsSchema },
|
|
48
56
|
async (
|
|
49
57
|
{ args }: { args: CreateOrdinalsArgs },
|
|
50
|
-
extra: RequestHandlerExtra,
|
|
58
|
+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
51
59
|
): Promise<CallToolResult> => {
|
|
52
60
|
try {
|
|
61
|
+
// Load optional identity key for sigma signing
|
|
62
|
+
const identityKeyWif = process.env.IDENTITY_KEY_WIF;
|
|
63
|
+
let identityPk: PrivateKey | undefined;
|
|
64
|
+
if (identityKeyWif) {
|
|
65
|
+
try {
|
|
66
|
+
identityPk = PrivateKey.fromWif(identityKeyWif);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
console.warn(
|
|
69
|
+
"Warning: Invalid IDENTITY_KEY_WIF environment variable; sigma signing disabled",
|
|
70
|
+
e,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
53
75
|
// 1. Get private key from wallet
|
|
54
76
|
const paymentPk = wallet.getPrivateKey();
|
|
55
77
|
if (!paymentPk) {
|
|
@@ -81,8 +103,7 @@ export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
|
|
|
81
103
|
},
|
|
82
104
|
];
|
|
83
105
|
|
|
84
|
-
|
|
85
|
-
const result = await createOrdinals({
|
|
106
|
+
const createOrdinalsConfig: CreateOrdinalsConfig = {
|
|
86
107
|
utxos: paymentUtxos,
|
|
87
108
|
destinations,
|
|
88
109
|
paymentPk,
|
|
@@ -91,35 +112,57 @@ export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
|
|
|
91
112
|
| PreMAP
|
|
92
113
|
| CreateOrdinalsCollectionMetadata
|
|
93
114
|
| CreateOrdinalsCollectionItemMetadata,
|
|
94
|
-
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
if (identityPk) {
|
|
118
|
+
createOrdinalsConfig.signer = {
|
|
119
|
+
idKey: identityPk,
|
|
120
|
+
} as LocalSigner;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 6. Create and broadcast the transaction
|
|
124
|
+
const result = await createOrdinals(createOrdinalsConfig);
|
|
95
125
|
|
|
96
126
|
const changeResult = result as ChangeResult;
|
|
97
127
|
|
|
98
|
-
// 7.
|
|
99
|
-
await changeResult.tx.broadcast();
|
|
128
|
+
// 7. Optionally sign with identity key and broadcast the transaction
|
|
100
129
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
await
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
)
|
|
130
|
+
const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
|
|
131
|
+
if (!disableBroadcasting) {
|
|
132
|
+
await changeResult.tx.broadcast();
|
|
133
|
+
|
|
134
|
+
// 8. Refresh the wallet's UTXOs after spending
|
|
135
|
+
try {
|
|
136
|
+
await wallet.refreshUtxos();
|
|
137
|
+
} catch (refreshError) {
|
|
138
|
+
console.warn(
|
|
139
|
+
"Failed to refresh UTXOs after transaction:",
|
|
140
|
+
refreshError,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 9. Return transaction details
|
|
145
|
+
return {
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
type: "text",
|
|
149
|
+
text: JSON.stringify({
|
|
150
|
+
txid: changeResult.tx.id("hex"),
|
|
151
|
+
spentOutpoints: changeResult.spentOutpoints,
|
|
152
|
+
payChange: changeResult.payChange,
|
|
153
|
+
inscriptionAddress: args.destinationAddress || walletAddress,
|
|
154
|
+
contentType: args.contentType,
|
|
155
|
+
}),
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
};
|
|
109
159
|
}
|
|
110
160
|
|
|
111
|
-
// 9. Return transaction details
|
|
112
161
|
return {
|
|
113
162
|
content: [
|
|
114
163
|
{
|
|
115
164
|
type: "text",
|
|
116
|
-
text:
|
|
117
|
-
txid: changeResult.tx.id("hex"),
|
|
118
|
-
spentOutpoints: changeResult.spentOutpoints,
|
|
119
|
-
payChange: changeResult.payChange,
|
|
120
|
-
inscriptionAddress: args.destinationAddress || walletAddress,
|
|
121
|
-
contentType: args.contentType,
|
|
122
|
-
}),
|
|
165
|
+
text: changeResult.tx.toHex(),
|
|
123
166
|
},
|
|
124
167
|
],
|
|
125
168
|
};
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
import { PrivateKey } from "@bsv/sdk";
|
|
2
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
4
|
+
import type {
|
|
5
|
+
ServerNotification,
|
|
6
|
+
ServerRequest,
|
|
7
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
4
8
|
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
5
9
|
import {
|
|
6
10
|
type ChangeResult,
|
|
7
11
|
type ExistingListing,
|
|
12
|
+
type LocalSigner,
|
|
8
13
|
type Payment,
|
|
14
|
+
type PurchaseOrdListingConfig,
|
|
9
15
|
type Royalty,
|
|
10
16
|
TokenType,
|
|
11
17
|
type TokenUtxo,
|
|
@@ -14,6 +20,7 @@ import {
|
|
|
14
20
|
purchaseOrdListing,
|
|
15
21
|
purchaseOrdTokenListing,
|
|
16
22
|
} from "js-1sat-ord";
|
|
23
|
+
import { Sigma } from "sigma-protocol";
|
|
17
24
|
import type { z } from "zod";
|
|
18
25
|
import {
|
|
19
26
|
MARKET_FEE_PERCENTAGE,
|
|
@@ -82,9 +89,23 @@ export function registerPurchaseListingTool(server: McpServer, wallet: Wallet) {
|
|
|
82
89
|
{ args: purchaseListingArgsSchema },
|
|
83
90
|
async (
|
|
84
91
|
{ args }: { args: z.infer<typeof purchaseListingArgsSchema> },
|
|
85
|
-
extra: RequestHandlerExtra,
|
|
92
|
+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
86
93
|
): Promise<CallToolResult> => {
|
|
87
94
|
try {
|
|
95
|
+
// Load optional identity key for sigma signing
|
|
96
|
+
const identityKeyWif = process.env.IDENTITY_KEY_WIF;
|
|
97
|
+
let identityPk: PrivateKey | undefined;
|
|
98
|
+
if (identityKeyWif) {
|
|
99
|
+
try {
|
|
100
|
+
identityPk = PrivateKey.fromWif(identityKeyWif);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
console.warn(
|
|
103
|
+
"Warning: Invalid IDENTITY_KEY_WIF environment variable; sigma signing disabled",
|
|
104
|
+
e,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
88
109
|
// Fetch the listing info directly from the API
|
|
89
110
|
const response = await fetch(
|
|
90
111
|
`https://ordinals.gorillapool.io/api/txos/${args.listingOutpoint}?script=true`,
|
|
@@ -233,7 +254,7 @@ Please fund this wallet address with enough BSV to cover the purchase price
|
|
|
233
254
|
}
|
|
234
255
|
}
|
|
235
256
|
|
|
236
|
-
|
|
257
|
+
const purchaseOrdListingConfig: PurchaseOrdListingConfig = {
|
|
237
258
|
utxos: paymentUtxos,
|
|
238
259
|
paymentPk,
|
|
239
260
|
ordAddress: args.ordAddress,
|
|
@@ -241,7 +262,9 @@ Please fund this wallet address with enough BSV to cover the purchase price
|
|
|
241
262
|
additionalPayments,
|
|
242
263
|
metaData,
|
|
243
264
|
royalties,
|
|
244
|
-
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
transaction = await purchaseOrdListing(purchaseOrdListingConfig);
|
|
245
268
|
}
|
|
246
269
|
|
|
247
270
|
// After successful transaction creation, refresh the wallet's UTXOs
|
|
@@ -252,44 +275,55 @@ Please fund this wallet address with enough BSV to cover the purchase price
|
|
|
252
275
|
// Remove console.warn
|
|
253
276
|
}
|
|
254
277
|
|
|
255
|
-
//
|
|
256
|
-
const broadcastResult = await transaction.tx.broadcast(
|
|
257
|
-
oneSatBroadcaster(),
|
|
258
|
-
);
|
|
278
|
+
// Optionally sign with identity key then broadcast the transaction
|
|
259
279
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
280
|
+
const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
|
|
281
|
+
if (!disableBroadcasting) {
|
|
282
|
+
const broadcastResult = await transaction.tx.broadcast(
|
|
283
|
+
oneSatBroadcaster(),
|
|
284
|
+
);
|
|
285
|
+
// Handle broadcast response
|
|
286
|
+
const resultStatus =
|
|
287
|
+
typeof broadcastResult === "object" && "status" in broadcastResult
|
|
288
|
+
? broadcastResult.status
|
|
289
|
+
: "unknown";
|
|
265
290
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
291
|
+
const resultMessage =
|
|
292
|
+
typeof broadcastResult === "object" && "error" in broadcastResult
|
|
293
|
+
? broadcastResult.error
|
|
294
|
+
: "Transaction broadcast successful";
|
|
270
295
|
|
|
296
|
+
return {
|
|
297
|
+
content: [
|
|
298
|
+
{
|
|
299
|
+
type: "text",
|
|
300
|
+
text: JSON.stringify({
|
|
301
|
+
status: resultStatus,
|
|
302
|
+
message: resultMessage,
|
|
303
|
+
txid: transaction.tx.id("hex"),
|
|
304
|
+
listingOutpoint: args.listingOutpoint,
|
|
305
|
+
destinationAddress: args.ordAddress,
|
|
306
|
+
listingType: args.listingType,
|
|
307
|
+
tokenProtocol: args.tokenID ? args.tokenProtocol : undefined,
|
|
308
|
+
tokenID: args.tokenID,
|
|
309
|
+
price: listingData.data.list.price,
|
|
310
|
+
marketFee,
|
|
311
|
+
marketFeeAddress: MARKET_WALLET_ADDRESS,
|
|
312
|
+
royaltiesPaid:
|
|
313
|
+
args.listingType === "nft" &&
|
|
314
|
+
listingData.origin?.data?.map?.royalties
|
|
315
|
+
? JSON.parse(listingData.origin.data.map.royalties)
|
|
316
|
+
: undefined,
|
|
317
|
+
}),
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
};
|
|
321
|
+
}
|
|
271
322
|
return {
|
|
272
323
|
content: [
|
|
273
324
|
{
|
|
274
325
|
type: "text",
|
|
275
|
-
text:
|
|
276
|
-
status: resultStatus,
|
|
277
|
-
message: resultMessage,
|
|
278
|
-
txid: transaction.tx.id("hex"),
|
|
279
|
-
listingOutpoint: args.listingOutpoint,
|
|
280
|
-
destinationAddress: args.ordAddress,
|
|
281
|
-
listingType: args.listingType,
|
|
282
|
-
tokenProtocol: args.tokenID ? args.tokenProtocol : undefined,
|
|
283
|
-
tokenID: args.tokenID,
|
|
284
|
-
price: listingData.data.list.price,
|
|
285
|
-
marketFee,
|
|
286
|
-
marketFeeAddress: MARKET_WALLET_ADDRESS,
|
|
287
|
-
royaltiesPaid:
|
|
288
|
-
args.listingType === "nft" &&
|
|
289
|
-
listingData.origin?.data?.map?.royalties
|
|
290
|
-
? JSON.parse(listingData.origin.data.map.royalties)
|
|
291
|
-
: undefined,
|
|
292
|
-
}),
|
|
326
|
+
text: transaction.tx.toHex(),
|
|
293
327
|
},
|
|
294
328
|
],
|
|
295
329
|
};
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
+
import { PrivateKey } from "@bsv/sdk";
|
|
1
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
3
|
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
CallToolResult,
|
|
6
|
+
ServerNotification,
|
|
7
|
+
ServerRequest,
|
|
8
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
4
9
|
import { sendOrdinals } from "js-1sat-ord";
|
|
5
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
ChangeResult,
|
|
12
|
+
LocalSigner,
|
|
13
|
+
SendOrdinalsConfig,
|
|
14
|
+
} from "js-1sat-ord";
|
|
15
|
+
import { Sigma } from "sigma-protocol";
|
|
6
16
|
import { z } from "zod";
|
|
7
17
|
import type { Wallet } from "./wallet";
|
|
8
18
|
|
|
@@ -37,7 +47,7 @@ export function registerSendOrdinalsTool(server: McpServer, wallet: Wallet) {
|
|
|
37
47
|
{ args: sendOrdinalsArgsSchema },
|
|
38
48
|
async (
|
|
39
49
|
{ args }: { args: SendOrdinalsArgs },
|
|
40
|
-
extra: RequestHandlerExtra,
|
|
50
|
+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
41
51
|
): Promise<CallToolResult> => {
|
|
42
52
|
try {
|
|
43
53
|
// 1. Get private key from wallet
|
|
@@ -86,6 +96,16 @@ export function registerSendOrdinalsTool(server: McpServer, wallet: Wallet) {
|
|
|
86
96
|
changeAddress: walletAddress,
|
|
87
97
|
};
|
|
88
98
|
|
|
99
|
+
const identityKeyWif = process.env.IDENTITY_KEY_WIF;
|
|
100
|
+
const identityPk = identityKeyWif
|
|
101
|
+
? PrivateKey.fromWif(identityKeyWif)
|
|
102
|
+
: undefined;
|
|
103
|
+
if (identityPk) {
|
|
104
|
+
sendOrdinalsConfig.signer = {
|
|
105
|
+
idKey: identityPk,
|
|
106
|
+
} as LocalSigner;
|
|
107
|
+
}
|
|
108
|
+
|
|
89
109
|
// Add metadata if provided
|
|
90
110
|
if (args.metadata) {
|
|
91
111
|
sendOrdinalsConfig.metaData = args.metadata;
|
|
@@ -96,32 +116,45 @@ export function registerSendOrdinalsTool(server: McpServer, wallet: Wallet) {
|
|
|
96
116
|
|
|
97
117
|
const result = await sendOrdinals(sendOrdinalsConfig);
|
|
98
118
|
const changeResult = result as ChangeResult;
|
|
119
|
+
// no signing when you send since we don't emit an inscription
|
|
99
120
|
|
|
100
121
|
// 7. Broadcast the transaction
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
122
|
+
const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
|
|
123
|
+
if (!disableBroadcasting) {
|
|
124
|
+
await changeResult.tx.broadcast();
|
|
125
|
+
|
|
126
|
+
// 8. Refresh the wallet's UTXOs after spending
|
|
127
|
+
try {
|
|
128
|
+
await wallet.refreshUtxos();
|
|
129
|
+
} catch (refreshError) {
|
|
130
|
+
console.warn(
|
|
131
|
+
"Failed to refresh UTXOs after transaction:",
|
|
132
|
+
refreshError,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 9. Return transaction details
|
|
137
|
+
return {
|
|
138
|
+
content: [
|
|
139
|
+
{
|
|
140
|
+
type: "text",
|
|
141
|
+
text: JSON.stringify({
|
|
142
|
+
txid: changeResult.tx.id("hex"),
|
|
143
|
+
spentOutpoints: changeResult.spentOutpoints,
|
|
144
|
+
payChange: changeResult.payChange,
|
|
145
|
+
inscriptionOutpoint: args.inscriptionOutpoint,
|
|
146
|
+
destinationAddress: args.destinationAddress,
|
|
147
|
+
}),
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
111
151
|
}
|
|
112
152
|
|
|
113
|
-
// 9. Return transaction details
|
|
114
153
|
return {
|
|
115
154
|
content: [
|
|
116
155
|
{
|
|
117
156
|
type: "text",
|
|
118
|
-
text:
|
|
119
|
-
txid: changeResult.tx.id("hex"),
|
|
120
|
-
spentOutpoints: changeResult.spentOutpoints,
|
|
121
|
-
payChange: changeResult.payChange,
|
|
122
|
-
inscriptionOutpoint: args.inscriptionOutpoint,
|
|
123
|
-
destinationAddress: args.destinationAddress,
|
|
124
|
-
}),
|
|
157
|
+
text: changeResult.tx.toHex(),
|
|
125
158
|
},
|
|
126
159
|
],
|
|
127
160
|
};
|
package/tools/wallet/tools.ts
CHANGED
|
@@ -99,6 +99,10 @@ type ToolHandlerMap = {
|
|
|
99
99
|
export function registerWalletTools(
|
|
100
100
|
server: McpServer,
|
|
101
101
|
wallet: Wallet,
|
|
102
|
+
config: {
|
|
103
|
+
disableBroadcasting: boolean;
|
|
104
|
+
enableA2bTools: boolean;
|
|
105
|
+
},
|
|
102
106
|
): ToolHandlerMap {
|
|
103
107
|
const handlers = {} as ToolHandlerMap;
|
|
104
108
|
|
|
@@ -129,11 +133,16 @@ export function registerWalletTools(
|
|
|
129
133
|
// Register the wallet_transferOrdToken tool
|
|
130
134
|
registerTransferOrdTokenTool(server, wallet);
|
|
131
135
|
|
|
132
|
-
//
|
|
133
|
-
|
|
136
|
+
// A2B tools have to be explicitly enabled
|
|
137
|
+
if (config.enableA2bTools) {
|
|
138
|
+
// Register the wallet_a2bPublishAgent tool
|
|
139
|
+
// registerA2bPublishAgentTool(server, wallet);
|
|
134
140
|
|
|
135
|
-
|
|
136
|
-
|
|
141
|
+
// Register the wallet_a2bPublishMcp tool
|
|
142
|
+
registerA2bPublishMcpTool(server, wallet, {
|
|
143
|
+
disableBroadcasting: config.disableBroadcasting,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
137
146
|
|
|
138
147
|
// Register only the minimal public-facing tools
|
|
139
148
|
// wallet_createAction, wallet_signAction and wallet_getHeight have been removed
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PrivateKey } from "@bsv/sdk";
|
|
1
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
3
|
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
4
|
import type {
|
|
@@ -6,6 +7,7 @@ import type {
|
|
|
6
7
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
8
|
import {
|
|
8
9
|
type Distribution,
|
|
10
|
+
type LocalSigner,
|
|
9
11
|
type Payment,
|
|
10
12
|
type TokenChangeResult,
|
|
11
13
|
TokenInputMode,
|
|
@@ -106,26 +108,47 @@ export function registerTransferOrdTokenTool(
|
|
|
106
108
|
},
|
|
107
109
|
};
|
|
108
110
|
|
|
111
|
+
const identityPk = process.env.IDENTITY_KEY_WIF
|
|
112
|
+
? PrivateKey.fromWif(process.env.IDENTITY_KEY_WIF)
|
|
113
|
+
: undefined;
|
|
114
|
+
|
|
115
|
+
if (identityPk) {
|
|
116
|
+
config.signer = {
|
|
117
|
+
idKey: identityPk,
|
|
118
|
+
} as LocalSigner;
|
|
119
|
+
}
|
|
120
|
+
|
|
109
121
|
// execute transfer
|
|
110
122
|
const result: TokenChangeResult = await transferOrdTokens(config);
|
|
111
|
-
|
|
123
|
+
const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
|
|
124
|
+
if (!disableBroadcasting) {
|
|
125
|
+
await result.tx.broadcast();
|
|
112
126
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
127
|
+
// refresh UTXOs
|
|
128
|
+
try {
|
|
129
|
+
await wallet.refreshUtxos();
|
|
130
|
+
} catch {}
|
|
117
131
|
|
|
118
|
-
|
|
132
|
+
// respond
|
|
133
|
+
return {
|
|
134
|
+
content: [
|
|
135
|
+
{
|
|
136
|
+
type: "text",
|
|
137
|
+
text: JSON.stringify({
|
|
138
|
+
txid: result.tx.id("hex"),
|
|
139
|
+
spentOutpoints: result.spentOutpoints,
|
|
140
|
+
payChange: result.payChange,
|
|
141
|
+
tokenChange: result.tokenChange,
|
|
142
|
+
}),
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
};
|
|
146
|
+
}
|
|
119
147
|
return {
|
|
120
148
|
content: [
|
|
121
149
|
{
|
|
122
150
|
type: "text",
|
|
123
|
-
text:
|
|
124
|
-
txid: result.tx.id("hex"),
|
|
125
|
-
spentOutpoints: result.spentOutpoints,
|
|
126
|
-
payChange: result.payChange,
|
|
127
|
-
tokenChange: result.tokenChange,
|
|
128
|
-
}),
|
|
151
|
+
text: result.tx.toHex(),
|
|
129
152
|
},
|
|
130
153
|
],
|
|
131
154
|
};
|
package/tools/wallet/wallet.ts
CHANGED
|
@@ -82,12 +82,20 @@ export class Wallet extends ProtoWallet implements WalletInterface {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
const address = privateKey.toAddress();
|
|
85
|
-
|
|
86
|
-
const utxos = await fetchPayUtxos(address);
|
|
87
|
-
const nftUtxos = await fetchNftUtxos(address);
|
|
88
|
-
this.paymentUtxos = utxos;
|
|
89
|
-
this.nftUtxos = nftUtxos;
|
|
90
85
|
this.lastUtxoFetch = Date.now();
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const utxos = await fetchPayUtxos(address);
|
|
89
|
+
this.paymentUtxos = utxos;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error("Error fetching payment UTXOs:", error);
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const nftUtxos = await fetchNftUtxos(address);
|
|
95
|
+
this.nftUtxos = nftUtxos;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error("Error fetching NFT UTXOs:", error);
|
|
98
|
+
}
|
|
91
99
|
} catch (error) {
|
|
92
100
|
console.error("Error refreshing UTXOs:", error);
|
|
93
101
|
throw error;
|