bsv-mcp 0.2.14 → 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 +90 -10
- package/README.md +13 -13
- package/dist/app.html +483 -106
- package/dist/index.js +265787 -155292
- package/index.ts +505 -70
- 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,31 +1,21 @@
|
|
|
1
|
-
import type { OneSatContext } from
|
|
2
|
-
import { sendBsv21,
|
|
3
|
-
import type { McpServer } from
|
|
4
|
-
import { z } from
|
|
5
|
-
|
|
6
|
-
const walletOutputSchema = z.object({
|
|
7
|
-
outpoint: z.string(),
|
|
8
|
-
satoshis: z.number().optional(),
|
|
9
|
-
tags: z.array(z.string()).optional(),
|
|
10
|
-
customInstructions: z.string().optional(),
|
|
11
|
-
lockingScript: z.string().optional(),
|
|
12
|
-
})
|
|
1
|
+
import type { OneSatContext } from "@1sat/actions";
|
|
2
|
+
import { sendBsv21, sendOrdinals } from "@1sat/actions";
|
|
3
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { assertBroadcastAllowed } from "../../utils/broadcastGuard";
|
|
13
6
|
|
|
14
7
|
export const transferOrdTokenArgsSchema = z.object({
|
|
15
|
-
type: z
|
|
16
|
-
"
|
|
17
|
-
),
|
|
18
|
-
// ordinal fields
|
|
19
|
-
ordinal: walletOutputSchema
|
|
20
|
-
.optional()
|
|
8
|
+
type: z
|
|
9
|
+
.enum(["ordinal", "bsv21"])
|
|
21
10
|
.describe(
|
|
22
|
-
"
|
|
11
|
+
"'ordinal' to transfer an inscription/NFT, 'bsv21' to send fungible BSV21 tokens",
|
|
23
12
|
),
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
// ordinal fields
|
|
14
|
+
id: z
|
|
15
|
+
.string()
|
|
26
16
|
.optional()
|
|
27
17
|
.describe(
|
|
28
|
-
"
|
|
18
|
+
"Tracking id of the ordinal in the ordinals basket (the 'id:' tag from wallet_getOrdinals). Required when type='ordinal'",
|
|
29
19
|
),
|
|
30
20
|
// bsv21 fields
|
|
31
21
|
tokenId: z
|
|
@@ -35,130 +25,128 @@ export const transferOrdTokenArgsSchema = z.object({
|
|
|
35
25
|
amount: z
|
|
36
26
|
.string()
|
|
37
27
|
.optional()
|
|
38
|
-
.describe(
|
|
28
|
+
.describe(
|
|
29
|
+
"Amount of tokens to send as a string integer. Required when type='bsv21'",
|
|
30
|
+
),
|
|
39
31
|
// shared destination fields
|
|
40
|
-
address: z.string().optional().describe(
|
|
32
|
+
address: z.string().optional().describe("Recipient P2PKH address"),
|
|
41
33
|
counterparty: z
|
|
42
34
|
.string()
|
|
43
35
|
.optional()
|
|
44
|
-
.describe(
|
|
45
|
-
})
|
|
36
|
+
.describe("Recipient identity public key (hex)"),
|
|
37
|
+
});
|
|
46
38
|
|
|
47
|
-
export type TransferOrdTokenArgs = z.infer<typeof transferOrdTokenArgsSchema
|
|
39
|
+
export type TransferOrdTokenArgs = z.infer<typeof transferOrdTokenArgsSchema>;
|
|
48
40
|
|
|
49
41
|
export function registerTransferOrdTokenTool(
|
|
50
42
|
server: McpServer,
|
|
51
43
|
ctx: OneSatContext | undefined,
|
|
52
44
|
) {
|
|
53
45
|
server.tool(
|
|
54
|
-
|
|
55
|
-
"Transfer an ordinal inscription or send BSV21 fungible tokens. Use type='ordinal' to transfer an NFT/inscription
|
|
46
|
+
"wallet_transferOrdToken",
|
|
47
|
+
"Transfer an ordinal inscription or send BSV21 fungible tokens. Use type='ordinal' to transfer an NFT/inscription by its ordinals-basket tracking id (from wallet_getOrdinals). Use type='bsv21' to send fungible tokens by token ID and amount.",
|
|
56
48
|
{ ...transferOrdTokenArgsSchema.shape },
|
|
57
|
-
async ({ type,
|
|
49
|
+
async ({ type, id, tokenId, amount, address, counterparty }) => {
|
|
58
50
|
if (!ctx) {
|
|
59
51
|
return {
|
|
60
52
|
content: [
|
|
61
53
|
{
|
|
62
|
-
type:
|
|
63
|
-
text:
|
|
54
|
+
type: "text",
|
|
55
|
+
text: "Wallet not initialized. Please configure a wallet before transferring.",
|
|
64
56
|
},
|
|
65
57
|
],
|
|
66
58
|
isError: true,
|
|
67
|
-
}
|
|
59
|
+
};
|
|
68
60
|
}
|
|
69
61
|
|
|
70
62
|
try {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
if (!address && !counterparty) {
|
|
63
|
+
assertBroadcastAllowed("wallet_transferOrdToken");
|
|
64
|
+
if (!address && !counterparty) {
|
|
65
|
+
return {
|
|
66
|
+
content: [
|
|
67
|
+
{ type: "text", text: "address or counterparty is required" },
|
|
68
|
+
],
|
|
69
|
+
isError: true,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (type === "ordinal") {
|
|
74
|
+
if (!id) {
|
|
85
75
|
return {
|
|
86
|
-
content: [
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: "text",
|
|
79
|
+
text: "id is required when type='ordinal'",
|
|
80
|
+
},
|
|
81
|
+
],
|
|
87
82
|
isError: true,
|
|
88
|
-
}
|
|
83
|
+
};
|
|
89
84
|
}
|
|
90
85
|
|
|
91
|
-
const result = await
|
|
92
|
-
transfers: [
|
|
93
|
-
|
|
94
|
-
ordinal: ordinal as Parameters<typeof transferOrdinals.execute>[1]['transfers'][0]['ordinal'],
|
|
95
|
-
address,
|
|
96
|
-
counterparty,
|
|
97
|
-
},
|
|
98
|
-
],
|
|
99
|
-
inputBEEF,
|
|
100
|
-
})
|
|
86
|
+
const result = await sendOrdinals.execute(ctx, {
|
|
87
|
+
transfers: [{ id, address, counterparty }],
|
|
88
|
+
});
|
|
101
89
|
|
|
102
90
|
if (result.error) {
|
|
103
91
|
return {
|
|
104
|
-
content: [{ type:
|
|
92
|
+
content: [{ type: "text", text: result.error }],
|
|
105
93
|
isError: true,
|
|
106
|
-
}
|
|
94
|
+
};
|
|
107
95
|
}
|
|
108
96
|
|
|
109
97
|
return {
|
|
110
|
-
content: [
|
|
111
|
-
|
|
98
|
+
content: [
|
|
99
|
+
{ type: "text", text: JSON.stringify({ txid: result.txid }) },
|
|
100
|
+
],
|
|
101
|
+
};
|
|
112
102
|
}
|
|
113
103
|
|
|
114
104
|
// bsv21
|
|
115
105
|
if (!tokenId) {
|
|
116
106
|
return {
|
|
117
|
-
content: [
|
|
107
|
+
content: [
|
|
108
|
+
{ type: "text", text: "tokenId is required when type='bsv21'" },
|
|
109
|
+
],
|
|
118
110
|
isError: true,
|
|
119
|
-
}
|
|
111
|
+
};
|
|
120
112
|
}
|
|
121
113
|
if (!amount) {
|
|
122
114
|
return {
|
|
123
|
-
content: [
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
if (!address && !counterparty) {
|
|
128
|
-
return {
|
|
129
|
-
content: [{ type: 'text', text: 'address or counterparty is required' }],
|
|
115
|
+
content: [
|
|
116
|
+
{ type: "text", text: "amount is required when type='bsv21'" },
|
|
117
|
+
],
|
|
130
118
|
isError: true,
|
|
131
|
-
}
|
|
119
|
+
};
|
|
132
120
|
}
|
|
133
121
|
|
|
134
122
|
const result = await sendBsv21.execute(ctx, {
|
|
135
123
|
tokenId,
|
|
136
|
-
amount,
|
|
137
|
-
|
|
138
|
-
counterparty,
|
|
139
|
-
})
|
|
124
|
+
recipients: [{ amount, destination: { address, counterparty } }],
|
|
125
|
+
});
|
|
140
126
|
|
|
141
127
|
if (result.error) {
|
|
142
128
|
return {
|
|
143
|
-
content: [{ type:
|
|
129
|
+
content: [{ type: "text", text: result.error }],
|
|
144
130
|
isError: true,
|
|
145
|
-
}
|
|
131
|
+
};
|
|
146
132
|
}
|
|
147
133
|
|
|
148
134
|
return {
|
|
149
|
-
content: [
|
|
150
|
-
|
|
135
|
+
content: [
|
|
136
|
+
{ type: "text", text: JSON.stringify({ txid: result.txid }) },
|
|
137
|
+
],
|
|
138
|
+
};
|
|
151
139
|
} catch (err: unknown) {
|
|
152
140
|
return {
|
|
153
141
|
content: [
|
|
154
142
|
{
|
|
155
|
-
type:
|
|
143
|
+
type: "text",
|
|
156
144
|
text: err instanceof Error ? err.message : String(err),
|
|
157
145
|
},
|
|
158
146
|
],
|
|
159
147
|
isError: true,
|
|
160
|
-
}
|
|
148
|
+
};
|
|
161
149
|
}
|
|
162
150
|
},
|
|
163
|
-
)
|
|
151
|
+
);
|
|
164
152
|
}
|
|
@@ -1,57 +1,63 @@
|
|
|
1
|
-
import type { OneSatContext } from
|
|
2
|
-
import { unlockBsv } from
|
|
3
|
-
import type { McpServer } from
|
|
1
|
+
import type { OneSatContext } from "@1sat/actions";
|
|
2
|
+
import { unlockBsv } from "@1sat/actions";
|
|
3
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { assertBroadcastAllowed } from "../../utils/broadcastGuard";
|
|
4
5
|
|
|
5
6
|
export function registerUnlockBsvTool(
|
|
6
7
|
server: McpServer,
|
|
7
8
|
ctx: OneSatContext | undefined,
|
|
8
9
|
) {
|
|
9
10
|
server.tool(
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
"wallet_unlockBsv",
|
|
12
|
+
"Unlock all matured time-locked BSV",
|
|
12
13
|
{},
|
|
13
14
|
async () => {
|
|
14
15
|
if (!ctx) {
|
|
15
16
|
return {
|
|
16
17
|
content: [
|
|
17
18
|
{
|
|
18
|
-
type:
|
|
19
|
-
text:
|
|
19
|
+
type: "text",
|
|
20
|
+
text: "Wallet not initialized. Please configure a wallet before unlocking.",
|
|
20
21
|
},
|
|
21
22
|
],
|
|
22
23
|
isError: true,
|
|
23
|
-
}
|
|
24
|
+
};
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
try {
|
|
27
|
-
|
|
28
|
+
assertBroadcastAllowed("wallet_unlockBsv");
|
|
29
|
+
const result = await unlockBsv.execute(ctx, {});
|
|
28
30
|
|
|
29
31
|
if (result.error) {
|
|
30
32
|
return {
|
|
31
|
-
content: [{ type:
|
|
33
|
+
content: [{ type: "text", text: result.error }],
|
|
32
34
|
isError: true,
|
|
33
|
-
}
|
|
35
|
+
};
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
return {
|
|
37
39
|
content: [
|
|
38
40
|
{
|
|
39
|
-
type:
|
|
40
|
-
text: JSON.stringify(
|
|
41
|
+
type: "text",
|
|
42
|
+
text: JSON.stringify(
|
|
43
|
+
result,
|
|
44
|
+
(_, v) => (typeof v === "bigint" ? v.toString() : v),
|
|
45
|
+
2,
|
|
46
|
+
),
|
|
41
47
|
},
|
|
42
48
|
],
|
|
43
|
-
}
|
|
49
|
+
};
|
|
44
50
|
} catch (err: unknown) {
|
|
45
51
|
return {
|
|
46
52
|
content: [
|
|
47
53
|
{
|
|
48
|
-
type:
|
|
54
|
+
type: "text",
|
|
49
55
|
text: err instanceof Error ? err.message : String(err),
|
|
50
56
|
},
|
|
51
57
|
],
|
|
52
58
|
isError: true,
|
|
53
|
-
}
|
|
59
|
+
};
|
|
54
60
|
}
|
|
55
61
|
},
|
|
56
|
-
)
|
|
62
|
+
);
|
|
57
63
|
}
|
package/tools/wallet/utxo.ts
CHANGED
package/tools/wallet/wallet.ts
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
import type { GetPublicKeyArgs, GetPublicKeyResult, PubKeyHex } from "@bsv/sdk";
|
|
10
10
|
import {
|
|
11
|
-
Beef,
|
|
12
11
|
type BroadcastFailure,
|
|
13
12
|
type BroadcastResponse,
|
|
14
13
|
fromUtxo,
|
|
@@ -16,7 +15,6 @@ import {
|
|
|
16
15
|
isBroadcastResponse,
|
|
17
16
|
P2PKH,
|
|
18
17
|
PrivateKey,
|
|
19
|
-
type PublicKey,
|
|
20
18
|
SatoshisPerKilobyte,
|
|
21
19
|
Script,
|
|
22
20
|
Transaction,
|
|
@@ -26,16 +24,6 @@ import { fetchNftUtxos, type NftUtxo, type Utxo } from "js-1sat-ord";
|
|
|
26
24
|
import { V5Broadcaster } from "../../utils/broadcaster";
|
|
27
25
|
import { fetchPaymentUtxosFromV5 } from "./fetchPaymentUtxos";
|
|
28
26
|
|
|
29
|
-
// Local interface for what encrypt/decrypt expect, will be mapped from Zod schema
|
|
30
|
-
interface InternalEncryptionArgs {
|
|
31
|
-
data: number[];
|
|
32
|
-
recipientPublicKey?: PublicKey; // For encryption
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
interface InternalDecryptionArgs {
|
|
36
|
-
data: number[];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
27
|
export class Wallet {
|
|
40
28
|
private paymentUtxos: Utxo[] = [];
|
|
41
29
|
private nftUtxos: NftUtxo[] = [];
|
|
@@ -146,7 +134,7 @@ export class Wallet {
|
|
|
146
134
|
return this.getPaymentKey()?.toAddress();
|
|
147
135
|
}
|
|
148
136
|
|
|
149
|
-
async getPublicKey(
|
|
137
|
+
async getPublicKey(_args?: GetPublicKeyArgs): Promise<GetPublicKeyResult> {
|
|
150
138
|
const currentPaymentKey = this.getPaymentKey();
|
|
151
139
|
if (!currentPaymentKey) {
|
|
152
140
|
throw new Error("No payment key available to derive public key.");
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import type { OneSatContext } from '@1sat/actions'
|
|
2
|
-
import { sendBsv } from '@1sat/actions'
|
|
3
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
4
|
-
import { toSatoshi } from 'satoshi-token'
|
|
5
|
-
import { getBsvPriceWithCache } from '../bsv/getPrice'
|
|
6
|
-
import { sendToAddressArgsSchema } from './schemas'
|
|
7
|
-
|
|
8
|
-
export function registerSendToAddressTool(
|
|
9
|
-
server: McpServer,
|
|
10
|
-
ctx: OneSatContext | undefined,
|
|
11
|
-
) {
|
|
12
|
-
server.tool(
|
|
13
|
-
'wallet_sendToAddress',
|
|
14
|
-
'Sends Bitcoin SV (BSV) to a specified address. This tool supports payments in both BSV and USD amounts (with automatic conversion using current exchange rates). Transaction fees are automatically calculated and a confirmation with transaction ID is returned upon success.',
|
|
15
|
-
{ ...sendToAddressArgsSchema.shape },
|
|
16
|
-
async ({ address, amount, currency = 'BSV', description = 'Send to address' }) => {
|
|
17
|
-
if (!ctx) {
|
|
18
|
-
return {
|
|
19
|
-
content: [
|
|
20
|
-
{
|
|
21
|
-
type: 'text',
|
|
22
|
-
text: 'Wallet not initialized. Please configure a wallet before sending.',
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
isError: true,
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
let satoshis: number
|
|
31
|
-
if (currency === 'USD') {
|
|
32
|
-
const bsvPriceUsd = await getBsvPriceWithCache()
|
|
33
|
-
satoshis = toSatoshi(amount / bsvPriceUsd)
|
|
34
|
-
} else {
|
|
35
|
-
satoshis = toSatoshi(amount)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const result = await sendBsv.execute(ctx, {
|
|
39
|
-
requests: [{ address, satoshis }],
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
if (result.error) {
|
|
43
|
-
return {
|
|
44
|
-
content: [{ type: 'text', text: result.error }],
|
|
45
|
-
isError: true,
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
content: [
|
|
51
|
-
{
|
|
52
|
-
type: 'text',
|
|
53
|
-
text: JSON.stringify({
|
|
54
|
-
status: 'success',
|
|
55
|
-
txid: result.txid,
|
|
56
|
-
satoshis,
|
|
57
|
-
}),
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
}
|
|
61
|
-
} catch (error) {
|
|
62
|
-
return {
|
|
63
|
-
content: [
|
|
64
|
-
{
|
|
65
|
-
type: 'text',
|
|
66
|
-
text: error instanceof Error ? error.message : String(error),
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
|
-
isError: true,
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
)
|
|
74
|
-
}
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import type { IntegratedWallet } from "./integratedWallet";
|
|
4
|
-
|
|
5
|
-
const setupDropletArgsSchema = z.object({
|
|
6
|
-
action: z.enum(["register_key", "create_faucet", "check_status"]),
|
|
7
|
-
faucetName: z.string().optional(),
|
|
8
|
-
fixedDropSats: z.number().optional(),
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
export type SetupDropletArgs = z.infer<typeof setupDropletArgsSchema>;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Register the setupDroplet tool for Droplet API initialization
|
|
15
|
-
*/
|
|
16
|
-
export function registerSetupDropletTool(
|
|
17
|
-
server: McpServer,
|
|
18
|
-
integratedWallet: IntegratedWallet,
|
|
19
|
-
) {
|
|
20
|
-
server.tool(
|
|
21
|
-
"wallet_setupDroplet",
|
|
22
|
-
"Setup and manage Droplet API integration. Actions: register_key (registers your public key), create_faucet (creates a new faucet), check_status (checks faucet status)",
|
|
23
|
-
{ ...setupDropletArgsSchema.shape },
|
|
24
|
-
async ({ action, faucetName, fixedDropSats }) => {
|
|
25
|
-
try {
|
|
26
|
-
const dropletClient = integratedWallet.getDropletClient();
|
|
27
|
-
if (!dropletClient) {
|
|
28
|
-
throw new Error("Droplet client not configured");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const config = dropletClient.getConfig();
|
|
32
|
-
const apiUrl = config.apiUrl;
|
|
33
|
-
const authKey = config.authKey;
|
|
34
|
-
|
|
35
|
-
switch (action) {
|
|
36
|
-
case "register_key": {
|
|
37
|
-
if (!authKey) {
|
|
38
|
-
throw new Error("No auth key configured");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const pubkey = authKey.toPublicKey().toString();
|
|
42
|
-
|
|
43
|
-
// Register the key with the Droplet API
|
|
44
|
-
const response = await fetch(`${apiUrl}/auth/register`, {
|
|
45
|
-
method: "POST",
|
|
46
|
-
headers: {
|
|
47
|
-
"Content-Type": "application/json",
|
|
48
|
-
},
|
|
49
|
-
body: JSON.stringify({ publicKey: pubkey }),
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
if (!response.ok) {
|
|
53
|
-
const error = await response.text();
|
|
54
|
-
throw new Error(`Failed to register key: ${error}`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
content: [
|
|
59
|
-
{
|
|
60
|
-
type: "text",
|
|
61
|
-
text: JSON.stringify({
|
|
62
|
-
status: "success",
|
|
63
|
-
message: "Key registered successfully",
|
|
64
|
-
publicKey: pubkey,
|
|
65
|
-
}),
|
|
66
|
-
},
|
|
67
|
-
],
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
case "create_faucet": {
|
|
72
|
-
if (!faucetName) {
|
|
73
|
-
throw new Error(
|
|
74
|
-
"faucetName is required for create_faucet action",
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// First, ensure the key is registered
|
|
79
|
-
if (authKey) {
|
|
80
|
-
const pubkey = authKey.toPublicKey().toString();
|
|
81
|
-
await fetch(`${apiUrl}/auth/register`, {
|
|
82
|
-
method: "POST",
|
|
83
|
-
headers: {
|
|
84
|
-
"Content-Type": "application/json",
|
|
85
|
-
},
|
|
86
|
-
body: JSON.stringify({ publicKey: pubkey }),
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Create the faucet
|
|
91
|
-
const body = {
|
|
92
|
-
name: faucetName,
|
|
93
|
-
fixed_drop_sats: fixedDropSats || 1000,
|
|
94
|
-
max_consolidation_inputs: 20,
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const headers = authKey
|
|
98
|
-
? await dropletClient.getAuthHeaders("POST", "/faucets", body)
|
|
99
|
-
: {};
|
|
100
|
-
|
|
101
|
-
const response = await fetch(`${apiUrl}/faucets`, {
|
|
102
|
-
method: "POST",
|
|
103
|
-
headers: {
|
|
104
|
-
"Content-Type": "application/json",
|
|
105
|
-
...headers,
|
|
106
|
-
},
|
|
107
|
-
body: JSON.stringify(body),
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
if (!response.ok) {
|
|
111
|
-
const error = await response.text();
|
|
112
|
-
throw new Error(`Failed to create faucet: ${error}`);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const result = await response.json();
|
|
116
|
-
return {
|
|
117
|
-
content: [
|
|
118
|
-
{
|
|
119
|
-
type: "text",
|
|
120
|
-
text: JSON.stringify({
|
|
121
|
-
status: "success",
|
|
122
|
-
message: "Faucet created successfully",
|
|
123
|
-
faucet: result,
|
|
124
|
-
}),
|
|
125
|
-
},
|
|
126
|
-
],
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
case "check_status": {
|
|
131
|
-
const status = await dropletClient.getFaucetStatus();
|
|
132
|
-
return {
|
|
133
|
-
content: [
|
|
134
|
-
{
|
|
135
|
-
type: "text",
|
|
136
|
-
text: JSON.stringify({
|
|
137
|
-
status: "success",
|
|
138
|
-
faucetStatus: status,
|
|
139
|
-
}),
|
|
140
|
-
},
|
|
141
|
-
],
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
default:
|
|
146
|
-
throw new Error(`Unknown action: ${action}`);
|
|
147
|
-
}
|
|
148
|
-
} catch (error) {
|
|
149
|
-
return {
|
|
150
|
-
content: [
|
|
151
|
-
{
|
|
152
|
-
type: "text",
|
|
153
|
-
text: error instanceof Error ? error.message : String(error),
|
|
154
|
-
},
|
|
155
|
-
],
|
|
156
|
-
isError: true,
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
);
|
|
161
|
-
}
|