bsv-mcp 0.2.15 → 0.3.0
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 +83 -10
- package/README.md +13 -13
- package/dist/app.html +483 -106
- package/dist/index.js +227181 -117395
- package/index.ts +463 -57
- package/package.json +24 -15
- package/prompts/bsvSdk/auth.ts +1 -1
- package/prompts/bsvSdk/cryptography.ts +1 -1
- package/prompts/bsvSdk/overview.ts +1 -1
- package/prompts/bsvSdk/primitives.ts +1 -1
- package/prompts/bsvSdk/script.ts +1 -1
- package/prompts/bsvSdk/transaction.ts +1 -1
- package/prompts/bsvSdk/wallet.ts +1 -1
- package/prompts/ordinals.ts +1 -1
- package/test-mcp-server.ts +26 -4
- package/tools/a2b/discover.ts +2 -2
- package/tools/bap/friend.ts +17 -24
- package/tools/bap/generate.test.ts +34 -32
- package/tools/bap/generate.ts +48 -43
- package/tools/bap/getCurrentAddress.ts +1 -3
- package/tools/bap/getId.test.ts +18 -10
- package/tools/bap/getId.ts +8 -6
- package/tools/bap/utils.ts +1 -1
- package/tools/bsocial/bmapReadPosts.ts +16 -2
- package/tools/bsocial/createPost.ts +21 -10
- package/tools/bsocial/readPosts.ts +10 -4
- package/tools/bsv/decodeTransaction.ts +1 -1
- package/tools/bsv/explore.ts +0 -4
- package/tools/bsv/getPrice.ts +1 -1
- package/tools/bsv/token.ts +14 -6
- package/tools/constants.ts +6 -2
- package/tools/index.ts +10 -10
- package/tools/mnee/getBalance.ts +1 -1
- package/tools/mnee/parseTx.ts +1 -1
- package/tools/mnee/sendMnee.ts +30 -8
- package/tools/ordinals/getInscription.ts +1 -3
- package/tools/ordinals/getTokenByIdOrTicker.ts +1 -3
- package/tools/ordinals/index.ts +4 -1
- package/tools/ordinals/marketListings.ts +1 -1
- package/tools/ordinals/marketSales.ts +1 -1
- package/tools/ordinals/searchInscriptions.ts +1 -1
- package/tools/utils/aip.ts +31 -22
- package/tools/utils/installAgentMaster.ts +8 -9
- package/tools/utils/toolRegistration.ts +21 -22
- package/tools/utils/transactionBuilder.ts +21 -14
- package/tools/wallet/a2bPublishMcp.ts +15 -5
- package/tools/wallet/brc100.ts +541 -310
- package/tools/wallet/cancelListing.ts +29 -37
- package/tools/wallet/createOrdinals.ts +43 -27
- package/tools/wallet/fetchPaymentUtxos.ts +7 -5
- package/tools/wallet/gatherCollectionInfo.ts +3 -5
- package/tools/wallet/getAddress.ts +1 -4
- package/tools/wallet/getBalance.ts +15 -15
- package/tools/wallet/{getBalanceDroplet.ts → getBalanceDroplit.ts} +6 -6
- package/tools/wallet/getBsv21Balances.ts +20 -14
- package/tools/wallet/getLockData.ts +11 -11
- package/tools/wallet/getOrdinals.ts +26 -20
- package/tools/wallet/integratedWallet.ts +55 -53
- package/tools/wallet/listOrdinal.ts +40 -39
- package/tools/wallet/listTokens.ts +18 -17
- package/tools/wallet/lockBsv.ts +36 -22
- package/tools/wallet/mintCollection.ts +15 -10
- package/tools/wallet/opnsDeregister.ts +29 -39
- package/tools/wallet/opnsRegister.ts +29 -39
- package/tools/wallet/purchaseListing.test.ts +27 -0
- package/tools/wallet/purchaseListing.ts +59 -40
- package/tools/wallet/refreshUtxos.ts +1 -1
- package/tools/wallet/schemas.ts +0 -13
- package/tools/wallet/sendAllBsv.ts +26 -20
- package/tools/wallet/sendBsv.ts +107 -0
- package/tools/wallet/setupDroplit.ts +157 -0
- package/tools/wallet/signBsm.ts +18 -18
- package/tools/wallet/sweepBsv.ts +48 -27
- package/tools/wallet/sweepBsv21.ts +42 -28
- package/tools/wallet/sweepOrdinals.ts +38 -26
- package/tools/wallet/tools.ts +11 -13
- package/tools/wallet/transactionHelper.ts +13 -5
- package/tools/wallet/transferOrdToken.ts +72 -84
- package/tools/wallet/unlockBsv.ts +23 -17
- package/tools/wallet/utxo.ts +1 -0
- package/tools/wallet/wallet.ts +1 -13
- package/tools/wallet/sendToAddress.ts +0 -74
- package/tools/wallet/setupDroplet.ts +0 -161
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { PrivateKey } from "@bsv/sdk";
|
|
2
2
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import type { Wallet } from "../wallet/wallet";
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
5
|
* Register the tool to get the BAP identity address
|
|
@@ -26,7 +24,7 @@ export function registerBapGetCurrentAddressTool(
|
|
|
26
24
|
if (identityKeyWifEnv) {
|
|
27
25
|
try {
|
|
28
26
|
pkToUse = PrivateKey.fromWif(identityKeyWifEnv);
|
|
29
|
-
} catch
|
|
27
|
+
} catch {
|
|
30
28
|
// Let the final check handle the error
|
|
31
29
|
}
|
|
32
30
|
}
|
package/tools/bap/getId.test.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, spyOn } from "bun:test";
|
|
2
2
|
import { fetchProfile } from "./getId";
|
|
3
3
|
import type { IdentityData, SigmaIdentityProfile } from "./types";
|
|
4
|
+
import { SchemaType } from "./types";
|
|
4
5
|
|
|
5
6
|
// Store the original fetch function
|
|
6
|
-
const
|
|
7
|
+
const _originalFetch = global.fetch;
|
|
7
8
|
let fetchMock: ReturnType<typeof spyOn>;
|
|
8
9
|
|
|
9
10
|
describe("BAP getId - fetchProfile", () => {
|
|
@@ -21,18 +22,25 @@ describe("BAP getId - fetchProfile", () => {
|
|
|
21
22
|
it("should return identity data for a valid idKey", async () => {
|
|
22
23
|
const mockIdKey = "testIdKey";
|
|
23
24
|
const mockIdentityData: IdentityData = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
idKey: mockIdKey,
|
|
26
|
+
rootAddress: "1SomeRootAddress",
|
|
27
|
+
currentAddress: "1SomeCurrentAddress",
|
|
28
|
+
addresses: [
|
|
27
29
|
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
bitcoinAddress: "1SomeBitcoinAddress",
|
|
30
|
+
address: "1SomeCurrentAddress",
|
|
31
|
+
txId: "a1b2c3d4".repeat(8),
|
|
32
|
+
block: 800000,
|
|
32
33
|
},
|
|
33
34
|
],
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
block: 800000,
|
|
36
|
+
firstSeen: 799999,
|
|
37
|
+
timestamp: 1700000000,
|
|
38
|
+
valid: true,
|
|
39
|
+
identity: {
|
|
40
|
+
"@context": "https://schema.org",
|
|
41
|
+
"@type": SchemaType.Person,
|
|
42
|
+
alternateName: "Test User",
|
|
43
|
+
},
|
|
36
44
|
};
|
|
37
45
|
const mockApiResponse: SigmaIdentityProfile = { result: mockIdentityData };
|
|
38
46
|
|
package/tools/bap/getId.ts
CHANGED
|
@@ -82,14 +82,16 @@ export function registerBapGetIdTool(
|
|
|
82
82
|
|
|
83
83
|
if (!targetIdKey) {
|
|
84
84
|
// First priority: Use authenticated user's BAP ID from OAuth session
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
const authExtra = extra.authInfo?.extra;
|
|
86
|
+
const authBapId = authExtra?.bapId;
|
|
87
|
+
const authPubkey = authExtra?.pubkey;
|
|
88
|
+
if (typeof authBapId === "string") {
|
|
89
|
+
targetIdKey = authBapId;
|
|
88
90
|
console.error(`Using authenticated user's BAP ID: ${targetIdKey}`);
|
|
89
91
|
}
|
|
90
92
|
// Second priority: Use authenticated user's pubkey
|
|
91
|
-
else if (
|
|
92
|
-
targetIdKey =
|
|
93
|
+
else if (typeof authPubkey === "string") {
|
|
94
|
+
targetIdKey = authPubkey;
|
|
93
95
|
console.error(`Using authenticated user's pubkey: ${targetIdKey}`);
|
|
94
96
|
}
|
|
95
97
|
// Fallback: Attempt to get idKey from the passed identityPk or environment variable
|
|
@@ -101,7 +103,7 @@ export function registerBapGetIdTool(
|
|
|
101
103
|
if (identityKeyWifEnv) {
|
|
102
104
|
try {
|
|
103
105
|
pkToUse = PrivateKey.fromWif(identityKeyWifEnv);
|
|
104
|
-
} catch
|
|
106
|
+
} catch {
|
|
105
107
|
// Don't error here, let it be handled by the final check
|
|
106
108
|
}
|
|
107
109
|
}
|
package/tools/bap/utils.ts
CHANGED
|
@@ -111,9 +111,23 @@ export function registerBmapReadPostsTool(server: McpServer) {
|
|
|
111
111
|
"bmap_readPosts",
|
|
112
112
|
"Read social posts from the BMAP API (query layer). Can fetch posts by author (BAP ID), specific post by transaction ID, or recent posts from all users. Supports pagination and feed functionality.",
|
|
113
113
|
{ ...bmapReadPostsArgsSchema.shape },
|
|
114
|
-
async ({
|
|
114
|
+
async ({
|
|
115
|
+
bapId,
|
|
116
|
+
txid,
|
|
117
|
+
limit,
|
|
118
|
+
page,
|
|
119
|
+
feed,
|
|
120
|
+
address,
|
|
121
|
+
}): Promise<CallToolResult> => {
|
|
115
122
|
try {
|
|
116
|
-
const result = await readBmapPosts({
|
|
123
|
+
const result = await readBmapPosts({
|
|
124
|
+
bapId,
|
|
125
|
+
txid,
|
|
126
|
+
limit,
|
|
127
|
+
page,
|
|
128
|
+
feed,
|
|
129
|
+
address,
|
|
130
|
+
});
|
|
117
131
|
|
|
118
132
|
if (result.success) {
|
|
119
133
|
return {
|
|
@@ -27,7 +27,9 @@ const createPostArgsSchema = z.object({
|
|
|
27
27
|
additionalMapData: z
|
|
28
28
|
.string()
|
|
29
29
|
.optional()
|
|
30
|
-
.describe(
|
|
30
|
+
.describe(
|
|
31
|
+
'Additional MAP protocol key-value pairs as a JSON object string, e.g. \'{"key": "value"}\'',
|
|
32
|
+
),
|
|
31
33
|
});
|
|
32
34
|
|
|
33
35
|
type CreatePostArgs = z.infer<typeof createPostArgsSchema>;
|
|
@@ -48,7 +50,6 @@ export async function createSocialPost(
|
|
|
48
50
|
const { content, contentType, app, additionalMapData } = args;
|
|
49
51
|
|
|
50
52
|
// Get wallet info
|
|
51
|
-
const address = await wallet.getAddress();
|
|
52
53
|
const paymentKey = wallet.getPaymentKey();
|
|
53
54
|
if (!paymentKey) {
|
|
54
55
|
return {
|
|
@@ -56,6 +57,7 @@ export async function createSocialPost(
|
|
|
56
57
|
error: "Payment key not available in wallet",
|
|
57
58
|
};
|
|
58
59
|
}
|
|
60
|
+
const address = paymentKey.toAddress();
|
|
59
61
|
|
|
60
62
|
// Prepare file extension based on content type
|
|
61
63
|
const fileExtension = contentType === "text/markdown" ? "md" : "txt";
|
|
@@ -71,7 +73,7 @@ export async function createSocialPost(
|
|
|
71
73
|
];
|
|
72
74
|
|
|
73
75
|
// Build MAP protocol data
|
|
74
|
-
const mapData:
|
|
76
|
+
const mapData: number[][] = [
|
|
75
77
|
toArray(MAP_PREFIX, "utf8"),
|
|
76
78
|
toArray("SET", "utf8"),
|
|
77
79
|
toArray("app", "utf8"),
|
|
@@ -82,7 +84,8 @@ export async function createSocialPost(
|
|
|
82
84
|
|
|
83
85
|
// Add any additional MAP data (parse JSON string if provided)
|
|
84
86
|
if (additionalMapData) {
|
|
85
|
-
const parsedMapData: Record<string, string> =
|
|
87
|
+
const parsedMapData: Record<string, string> =
|
|
88
|
+
JSON.parse(additionalMapData);
|
|
86
89
|
for (const [key, value] of Object.entries(parsedMapData)) {
|
|
87
90
|
mapData.push(toArray(key, "utf8"), toArray(value, "utf8"));
|
|
88
91
|
}
|
|
@@ -96,20 +99,20 @@ export async function createSocialPost(
|
|
|
96
99
|
const { signedData } = await signOpReturnWithAIP(
|
|
97
100
|
dataToSign,
|
|
98
101
|
paymentKey,
|
|
99
|
-
address
|
|
102
|
+
address,
|
|
100
103
|
);
|
|
101
104
|
|
|
102
105
|
// Create OP_RETURN script
|
|
103
106
|
const script = buildOpReturnScript(signedData);
|
|
104
107
|
|
|
105
108
|
// Get UTXOs
|
|
106
|
-
const
|
|
109
|
+
const { paymentUtxos } = await wallet.getUtxos();
|
|
107
110
|
|
|
108
111
|
// Build and send transaction using the new utility
|
|
109
112
|
return await buildAndSendTransaction({
|
|
110
113
|
outputs: [{ script, satoshis: 0 }],
|
|
111
|
-
utxos,
|
|
112
|
-
changeAddress: address
|
|
114
|
+
utxos: paymentUtxos,
|
|
115
|
+
changeAddress: address,
|
|
113
116
|
paymentKey,
|
|
114
117
|
});
|
|
115
118
|
} catch (error) {
|
|
@@ -129,8 +132,16 @@ export function registerCreatePostTool(server: McpServer, wallet: Wallet) {
|
|
|
129
132
|
"bsocial_createPost",
|
|
130
133
|
"Create a social post on the BSV blockchain using B:// and MAP protocols. Posts are stored permanently on-chain and can include plain text or markdown content.",
|
|
131
134
|
{ ...createPostArgsSchema.shape },
|
|
132
|
-
async ({
|
|
133
|
-
|
|
135
|
+
async ({
|
|
136
|
+
content,
|
|
137
|
+
contentType,
|
|
138
|
+
app,
|
|
139
|
+
additionalMapData,
|
|
140
|
+
}): Promise<CallToolResult> => {
|
|
141
|
+
const result = await createSocialPost(
|
|
142
|
+
{ content, contentType, app, additionalMapData },
|
|
143
|
+
wallet,
|
|
144
|
+
);
|
|
134
145
|
return createResponse(result);
|
|
135
146
|
},
|
|
136
147
|
);
|
|
@@ -83,9 +83,9 @@ export async function readSocialPosts(args: ReadPostsArgs): Promise<{
|
|
|
83
83
|
|
|
84
84
|
return {
|
|
85
85
|
success: true,
|
|
86
|
-
posts: [data.
|
|
87
|
-
signers: data.
|
|
88
|
-
meta: [data.
|
|
86
|
+
posts: [data.data.post],
|
|
87
|
+
signers: data.data.signers,
|
|
88
|
+
meta: [data.data.meta],
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -201,7 +201,13 @@ export function registerReadPostsTool(server: McpServer) {
|
|
|
201
201
|
{ ...readPostsArgsSchema.shape },
|
|
202
202
|
async ({ bapId, txid, limit, page, feed }): Promise<CallToolResult> => {
|
|
203
203
|
try {
|
|
204
|
-
const result = await readSocialPosts({
|
|
204
|
+
const result = await readSocialPosts({
|
|
205
|
+
bapId,
|
|
206
|
+
txid,
|
|
207
|
+
limit,
|
|
208
|
+
page,
|
|
209
|
+
feed,
|
|
210
|
+
});
|
|
205
211
|
|
|
206
212
|
if (result.success && result.posts) {
|
|
207
213
|
// Format posts for display
|
|
@@ -126,7 +126,7 @@ export function registerDecodeTransactionTool(server: McpServer): void {
|
|
|
126
126
|
decodeTransactionArgsSchema.shape,
|
|
127
127
|
async (
|
|
128
128
|
{ tx, encoding },
|
|
129
|
-
|
|
129
|
+
_extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
130
130
|
) => {
|
|
131
131
|
try {
|
|
132
132
|
let transaction: Transaction;
|
package/tools/bsv/explore.ts
CHANGED
|
@@ -94,9 +94,6 @@ const exploreArgsSchema = z.object({
|
|
|
94
94
|
.describe("Number of days for miner stats endpoints (defaults to 7)"),
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
-
// Type for the tool arguments
|
|
98
|
-
type ExploreArgs = z.infer<typeof exploreArgsSchema>;
|
|
99
|
-
|
|
100
97
|
/**
|
|
101
98
|
* Register the bsv_explore tool with the MCP server
|
|
102
99
|
* @param server The MCP server instance
|
|
@@ -134,7 +131,6 @@ export function registerExploreTool(server: McpServer): void {
|
|
|
134
131
|
exploreArgsSchema.shape,
|
|
135
132
|
async (params) => {
|
|
136
133
|
try {
|
|
137
|
-
|
|
138
134
|
// Validate required parameters for specific endpoints
|
|
139
135
|
if (
|
|
140
136
|
params.endpoint === ExploreEndpoint.BLOCK_BY_HASH &&
|
package/tools/bsv/getPrice.ts
CHANGED
package/tools/bsv/token.ts
CHANGED
|
@@ -17,7 +17,9 @@ export function registerTokenTools(server: McpServer): void {
|
|
|
17
17
|
server.tool(
|
|
18
18
|
"bsv_toSatoshi",
|
|
19
19
|
{
|
|
20
|
-
bitcoin: z
|
|
20
|
+
bitcoin: z
|
|
21
|
+
.string()
|
|
22
|
+
.describe("Bitcoin amount (accepts numeric strings like '1.5')"),
|
|
21
23
|
returnType: z.enum(["number", "string", "bigint"]).optional(),
|
|
22
24
|
},
|
|
23
25
|
async ({ bitcoin, returnType }) => {
|
|
@@ -46,7 +48,9 @@ export function registerTokenTools(server: McpServer): void {
|
|
|
46
48
|
server.tool(
|
|
47
49
|
"bsv_toBitcoin",
|
|
48
50
|
{
|
|
49
|
-
satoshis: z
|
|
51
|
+
satoshis: z
|
|
52
|
+
.string()
|
|
53
|
+
.describe("Satoshi amount (accepts numeric strings like '100000000')"),
|
|
50
54
|
returnType: z.enum(["number", "string", "bigint"]).optional(),
|
|
51
55
|
},
|
|
52
56
|
async ({ satoshis, returnType }) => {
|
|
@@ -58,7 +62,7 @@ export function registerTokenTools(server: McpServer): void {
|
|
|
58
62
|
try {
|
|
59
63
|
result = toBitcoin(satoshis, ReturnTypes.BigInt);
|
|
60
64
|
return { content: [{ type: "text", text: result.toString() }] };
|
|
61
|
-
} catch
|
|
65
|
+
} catch {
|
|
62
66
|
return {
|
|
63
67
|
content: [
|
|
64
68
|
{
|
|
@@ -87,7 +91,9 @@ export function registerTokenTools(server: McpServer): void {
|
|
|
87
91
|
server.tool(
|
|
88
92
|
"bsv_toTokenSatoshi",
|
|
89
93
|
{
|
|
90
|
-
token: z
|
|
94
|
+
token: z
|
|
95
|
+
.string()
|
|
96
|
+
.describe("Token amount (accepts numeric strings like '1.5')"),
|
|
91
97
|
decimals: z.number().int().min(0),
|
|
92
98
|
returnType: z.enum(["number", "string", "bigint"]).optional(),
|
|
93
99
|
},
|
|
@@ -117,7 +123,9 @@ export function registerTokenTools(server: McpServer): void {
|
|
|
117
123
|
server.tool(
|
|
118
124
|
"bsv_toToken",
|
|
119
125
|
{
|
|
120
|
-
tokenSatoshi: z
|
|
126
|
+
tokenSatoshi: z
|
|
127
|
+
.string()
|
|
128
|
+
.describe("Token satoshi amount (accepts numeric strings)"),
|
|
121
129
|
decimals: z.number().int().min(0),
|
|
122
130
|
returnType: z.enum(["number", "string", "bigint"]).optional(),
|
|
123
131
|
},
|
|
@@ -130,7 +138,7 @@ export function registerTokenTools(server: McpServer): void {
|
|
|
130
138
|
try {
|
|
131
139
|
result = toToken(tokenSatoshi, decimals, ReturnTypes.BigInt);
|
|
132
140
|
return { content: [{ type: "text", text: result.toString() }] };
|
|
133
|
-
} catch
|
|
141
|
+
} catch {
|
|
134
142
|
return {
|
|
135
143
|
content: [
|
|
136
144
|
{
|
package/tools/constants.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Constants for BSV MCP Tools
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { AIP_BITCOM_ADDRESS } from "bsv-bap";
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* Market fee percentage applied to all marketplace purchases
|
|
7
9
|
* Expressed as a decimal (e.g., 0.03 = 3%)
|
|
@@ -23,7 +25,9 @@ export const MINIMUM_MARKET_FEE_SATOSHIS = 10000; // 10000 satoshis = 0.0001 BSV
|
|
|
23
25
|
export const BAP_PREFIX = "1BAPSuaPnfGnSBM3GLV9yhxUdYe4vGbdMT";
|
|
24
26
|
export const B_PREFIX = "19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut";
|
|
25
27
|
export const MAP_PREFIX = "1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5";
|
|
26
|
-
|
|
28
|
+
// Sourced from bsv-bap so it stays in step with the verifiers (bsv-bap,
|
|
29
|
+
// @1sat/templates, go-templates) rather than drifting from them.
|
|
30
|
+
export const AIP_PREFIX: string = AIP_BITCOM_ADDRESS;
|
|
27
31
|
export const STORAGE_MNEMONIC_KEY = "id_mnemonic";
|
|
28
32
|
export const PUBLIC_URL = process.env.PUBLIC_URL || "";
|
|
29
33
|
export const BMAP_URL =
|
|
@@ -38,5 +42,5 @@ export const YOURS_INSTALL_URL =
|
|
|
38
42
|
// process.env.PUBLIC_BSOCIAL_API_URL ||
|
|
39
43
|
// "https://bsocial-overlay-production.up.railway.app/api/v1";
|
|
40
44
|
|
|
41
|
-
export const BSOCIAL_API_URL = "https://
|
|
45
|
+
export const BSOCIAL_API_URL = "https://sigma.1sat.app/1sat/bap";
|
|
42
46
|
export const V5_API_URL = "https://ordinals.1sat.app/v5";
|
package/tools/index.ts
CHANGED
|
@@ -9,9 +9,9 @@ import { registerBsvTools } from "./bsv";
|
|
|
9
9
|
import { registerMneeTools } from "./mnee";
|
|
10
10
|
import { registerOrdinalsTools } from "./ordinals";
|
|
11
11
|
import { registerUtilsTools } from "./utils";
|
|
12
|
-
import {
|
|
12
|
+
import { registerWalletGetBalanceDroplitTool } from "./wallet/getBalanceDroplit";
|
|
13
13
|
import type { IntegratedWallet } from "./wallet/integratedWallet";
|
|
14
|
-
import {
|
|
14
|
+
import { registerSetupDroplitTools } from "./wallet/setupDroplit";
|
|
15
15
|
import { registerWalletTools } from "./wallet/tools";
|
|
16
16
|
import type { Wallet } from "./wallet/wallet";
|
|
17
17
|
|
|
@@ -116,16 +116,16 @@ export function registerAllTools(
|
|
|
116
116
|
|
|
117
117
|
// Register Wallet tools themselves
|
|
118
118
|
if (enableWalletTools) {
|
|
119
|
-
if (config.integratedWallet?.
|
|
120
|
-
// Register
|
|
121
|
-
const
|
|
122
|
-
if (
|
|
123
|
-
// Register
|
|
124
|
-
|
|
119
|
+
if (config.integratedWallet?.isDroplitMode) {
|
|
120
|
+
// Register Droplit-mode wallet tools
|
|
121
|
+
const droplitClient = config.integratedWallet.getDroplitClient();
|
|
122
|
+
if (droplitClient) {
|
|
123
|
+
// Register Droplit-specific tools
|
|
124
|
+
registerWalletGetBalanceDroplitTool(server, droplitClient);
|
|
125
125
|
if (config.integratedWallet) {
|
|
126
|
-
|
|
126
|
+
registerSetupDroplitTools(server, config.integratedWallet);
|
|
127
127
|
}
|
|
128
|
-
console.error("Registered
|
|
128
|
+
console.error("Registered Droplit mode wallet tools");
|
|
129
129
|
}
|
|
130
130
|
} else if (config.wallet) {
|
|
131
131
|
// Register normal wallet tools
|
package/tools/mnee/getBalance.ts
CHANGED
package/tools/mnee/parseTx.ts
CHANGED
|
@@ -27,7 +27,7 @@ export function registerParseTxTool(
|
|
|
27
27
|
{ ...parseTxArgsSchema.shape },
|
|
28
28
|
async (
|
|
29
29
|
{ txid },
|
|
30
|
-
|
|
30
|
+
_extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
31
31
|
): Promise<CallToolResult> => {
|
|
32
32
|
try {
|
|
33
33
|
const result: ParseTxResponse = await mnee.parseTx(txid);
|
package/tools/mnee/sendMnee.ts
CHANGED
|
@@ -8,13 +8,18 @@ import type {
|
|
|
8
8
|
import type Mnee from "mnee";
|
|
9
9
|
import type { SendMNEE, TransferResponse } from "mnee";
|
|
10
10
|
import { z } from "zod";
|
|
11
|
+
import { assertBroadcastAllowed } from "../../utils/broadcastGuard";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Schema for the sendMnee tool arguments.
|
|
14
15
|
*/
|
|
15
16
|
export const sendMneeArgsSchema = z.object({
|
|
16
|
-
address: z.string().describe("The recipient's address"),
|
|
17
|
-
amount: z
|
|
17
|
+
address: z.string().min(1).describe("The recipient's address"),
|
|
18
|
+
amount: z
|
|
19
|
+
.number()
|
|
20
|
+
.positive()
|
|
21
|
+
.finite()
|
|
22
|
+
.describe("Amount to send; must be greater than zero"),
|
|
18
23
|
currency: z
|
|
19
24
|
.enum(["MNEE", "USD"])
|
|
20
25
|
.default("MNEE")
|
|
@@ -45,10 +50,13 @@ export function registerSendMneeTool(server: McpServer, mnee: Mnee): void {
|
|
|
45
50
|
{ ...sendMneeArgsSchema.shape },
|
|
46
51
|
async (
|
|
47
52
|
{ address, amount, currency },
|
|
48
|
-
|
|
53
|
+
_extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
49
54
|
): Promise<CallToolResult> => {
|
|
50
55
|
try {
|
|
51
|
-
|
|
56
|
+
assertBroadcastAllowed("mnee_sendMnee");
|
|
57
|
+
// MNEE is dollar-pegged, so a USD amount transfers one-for-one. The
|
|
58
|
+
// currency the caller named is echoed in the response rather than
|
|
59
|
+
// dropped, so the interpretation is visible to whoever called.
|
|
52
60
|
const mneeAmount = amount;
|
|
53
61
|
|
|
54
62
|
const transferRequest: SendMNEE[] = [
|
|
@@ -81,13 +89,25 @@ export function registerSendMneeTool(server: McpServer, mnee: Mnee): void {
|
|
|
81
89
|
};
|
|
82
90
|
}
|
|
83
91
|
|
|
92
|
+
// transfer() throws on failure. On success it returns the cosigner's
|
|
93
|
+
// broadcast ticket id — the txid is only known once the ticket
|
|
94
|
+
// resolves, so it is read back via getTxStatus.
|
|
84
95
|
const result: TransferResponse = await mnee.transfer(
|
|
85
96
|
transferRequest,
|
|
86
97
|
wif,
|
|
87
98
|
);
|
|
88
99
|
|
|
89
|
-
if (result.
|
|
90
|
-
throw new Error(
|
|
100
|
+
if (!result.ticketId) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
"MNEE transfer returned no ticket id; the transaction was not submitted for broadcast.",
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const status = await mnee.getTxStatus(result.ticketId);
|
|
107
|
+
if (status.status === "FAILED") {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`MNEE transfer ${result.ticketId} failed: ${status.errors ?? "no error detail returned"}`,
|
|
110
|
+
);
|
|
91
111
|
}
|
|
92
112
|
|
|
93
113
|
return {
|
|
@@ -97,8 +117,10 @@ export function registerSendMneeTool(server: McpServer, mnee: Mnee): void {
|
|
|
97
117
|
text: JSON.stringify(
|
|
98
118
|
{
|
|
99
119
|
success: true,
|
|
100
|
-
|
|
101
|
-
|
|
120
|
+
ticketId: result.ticketId,
|
|
121
|
+
txid: status.tx_id,
|
|
122
|
+
status: status.status,
|
|
123
|
+
requestedCurrency: currency,
|
|
102
124
|
mneeAmount: mneeAmount,
|
|
103
125
|
usdAmount: formatUSD(mneeAmount),
|
|
104
126
|
recipient: address,
|
|
@@ -13,9 +13,7 @@ export function registerGetInscriptionTool(
|
|
|
13
13
|
"ordinals_getInscription",
|
|
14
14
|
"Retrieves metadata for an inscription by its outpoint. Returns content type, file info, origin, MAP data, and sequence info.",
|
|
15
15
|
{
|
|
16
|
-
outpoint: z
|
|
17
|
-
.string()
|
|
18
|
-
.describe("Outpoint in format 'txid_vout'"),
|
|
16
|
+
outpoint: z.string().describe("Outpoint in format 'txid_vout'"),
|
|
19
17
|
},
|
|
20
18
|
async ({ outpoint }) => {
|
|
21
19
|
try {
|
|
@@ -13,9 +13,7 @@ export function registerGetTokenByIdOrTickerTool(
|
|
|
13
13
|
"ordinals_getTokenByIdOrTicker",
|
|
14
14
|
"Retrieves detailed information about a BSV21 token by its ID (txid_vout format). Returns token data including symbol, supply, decimals, funding status, and current state.",
|
|
15
15
|
{
|
|
16
|
-
id: z
|
|
17
|
-
.string()
|
|
18
|
-
.describe("BSV21 token ID in outpoint format (txid_vout)"),
|
|
16
|
+
id: z.string().describe("BSV21 token ID in outpoint format (txid_vout)"),
|
|
19
17
|
},
|
|
20
18
|
async ({ id }) => {
|
|
21
19
|
try {
|
package/tools/ordinals/index.ts
CHANGED
|
@@ -11,7 +11,10 @@ import { registerSearchInscriptionsTool } from "./searchInscriptions";
|
|
|
11
11
|
* @param server The MCP server instance
|
|
12
12
|
* @param ctx OneSat context for SDK service access
|
|
13
13
|
*/
|
|
14
|
-
export function registerOrdinalsTools(
|
|
14
|
+
export function registerOrdinalsTools(
|
|
15
|
+
server: McpServer,
|
|
16
|
+
ctx?: OneSatContext,
|
|
17
|
+
): void {
|
|
15
18
|
registerGetInscriptionTool(server, ctx);
|
|
16
19
|
registerSearchInscriptionsTool(server, ctx);
|
|
17
20
|
registerMarketListingsTool(server, ctx);
|
package/tools/utils/aip.ts
CHANGED
|
@@ -1,45 +1,54 @@
|
|
|
1
|
-
import { BSM, type PrivateKey, Utils } from "@bsv/sdk";
|
|
1
|
+
import { BigNumber, BSM, OP, type PrivateKey, Utils } from "@bsv/sdk";
|
|
2
2
|
import { AIP_PREFIX } from "../constants";
|
|
3
3
|
|
|
4
|
-
const { toArray
|
|
4
|
+
const { toArray } = Utils;
|
|
5
|
+
|
|
6
|
+
const PIPE = 0x7c;
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Sign OP_RETURN data with AIP (Author Identity Protocol)
|
|
8
|
-
*
|
|
10
|
+
*
|
|
11
|
+
* The signed message is the OP_RETURN opcode, then every push payload, then
|
|
12
|
+
* the `|` protocol separator — the reconstruction every AIP verifier performs
|
|
13
|
+
* (bsv-bap `verifyAttestationWithAIP`, `@1sat/templates` `AIP.validateAIP`,
|
|
14
|
+
* go-templates). The signature is written as raw compact bytes, not as base64
|
|
15
|
+
* text; verifiers base64-encode the pushdata themselves.
|
|
16
|
+
*
|
|
17
|
+
* @param dataArrays - Push payloads that follow OP_RETURN
|
|
9
18
|
* @param signingKey - Private key to sign with
|
|
10
19
|
* @param signingAddress - Address associated with the signing key
|
|
11
|
-
* @returns Object containing
|
|
20
|
+
* @returns Object containing the payloads with the AIP fields appended
|
|
12
21
|
*/
|
|
13
22
|
export async function signOpReturnWithAIP(
|
|
14
|
-
dataArrays:
|
|
23
|
+
dataArrays: number[][],
|
|
15
24
|
signingKey: PrivateKey,
|
|
16
25
|
signingAddress: string,
|
|
17
|
-
): Promise<{ signedData:
|
|
18
|
-
|
|
19
|
-
let messageToSign = new Uint8Array();
|
|
20
|
-
for (const data of dataArrays) {
|
|
21
|
-
const temp = new Uint8Array(messageToSign.length + data.length);
|
|
22
|
-
temp.set(messageToSign);
|
|
23
|
-
temp.set(data, messageToSign.length);
|
|
24
|
-
messageToSign = temp;
|
|
25
|
-
}
|
|
26
|
+
): Promise<{ signedData: number[][] }> {
|
|
27
|
+
const messageToSign = [OP.OP_RETURN, ...dataArrays.flat(), PIPE];
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
const signature = BSM.sign(messageToSign, signingKey, "raw");
|
|
30
|
+
if (typeof signature === "string") {
|
|
31
|
+
throw new Error("BSM.sign returned a string in raw mode");
|
|
32
|
+
}
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
const
|
|
34
|
+
const magicHash = new BigNumber(BSM.magicHash(messageToSign));
|
|
35
|
+
const recovery = signature.CalculateRecoveryFactor(
|
|
36
|
+
signingKey.toPublicKey(),
|
|
37
|
+
magicHash,
|
|
38
|
+
);
|
|
39
|
+
const compactSignature = signature.toCompact(recovery, true);
|
|
40
|
+
if (!Array.isArray(compactSignature)) {
|
|
41
|
+
throw new Error("Signature.toCompact returned a string without encoding");
|
|
42
|
+
}
|
|
32
43
|
|
|
33
|
-
|
|
34
|
-
const aipData: Uint8Array[] = [
|
|
44
|
+
const aipData: number[][] = [
|
|
35
45
|
toArray("|", "utf8"), // Separator
|
|
36
46
|
toArray(AIP_PREFIX, "utf8"),
|
|
37
47
|
toArray("BITCOIN_ECDSA", "utf8"),
|
|
38
48
|
toArray(signingAddress, "utf8"),
|
|
39
|
-
|
|
49
|
+
compactSignature,
|
|
40
50
|
];
|
|
41
51
|
|
|
42
|
-
// Return combined data
|
|
43
52
|
return {
|
|
44
53
|
signedData: [...dataArrays, ...aipData],
|
|
45
54
|
};
|