bsv-mcp 0.2.9 → 0.2.11
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 +19 -0
- package/README.md +26 -2
- package/dist/index.js +6777 -126216
- package/index.ts +287 -265
- package/package.json +4 -3
- package/tools/bap/generate.test.ts +17 -16
- package/tools/index.ts +8 -3
- package/tools/ordinals/getInscription.ts +15 -65
- package/tools/ordinals/getTokenByIdOrTicker.ts +14 -80
- package/tools/ordinals/index.ts +8 -7
- package/tools/ordinals/marketListings.ts +21 -188
- package/tools/ordinals/marketSales.ts +21 -127
- package/tools/ordinals/searchInscriptions.ts +21 -108
- package/tools/wallet/brc100.ts +572 -0
- package/tools/wallet/cancelListing.ts +80 -0
- package/tools/wallet/createOrdinals.ts +62 -143
- package/tools/wallet/getAddress.ts +19 -13
- package/tools/wallet/getBalance.ts +29 -25
- package/tools/wallet/getBsv21Balances.ts +28 -0
- package/tools/wallet/getLockData.ts +25 -0
- package/tools/wallet/getOrdinals.ts +35 -0
- package/tools/wallet/listOrdinal.ts +86 -0
- package/tools/wallet/listTokens.ts +28 -0
- package/tools/wallet/lockBsv.ts +69 -0
- package/tools/wallet/opnsDeregister.ts +80 -0
- package/tools/wallet/opnsRegister.ts +80 -0
- package/tools/wallet/purchaseListing.ts +89 -290
- package/tools/wallet/refreshUtxos.ts +21 -32
- package/tools/wallet/schemas.ts +7 -11
- package/tools/wallet/sendAllBsv.ts +64 -0
- package/tools/wallet/sendToAddress.ts +46 -47
- package/tools/wallet/signBsm.ts +29 -0
- package/tools/wallet/sweepBsv.ts +70 -0
- package/tools/wallet/sweepBsv21.ts +71 -0
- package/tools/wallet/sweepOrdinals.ts +69 -0
- package/tools/wallet/tools.ts +50 -88
- package/tools/wallet/transferOrdToken.ts +137 -129
- package/tools/wallet/unlockBsv.ts +57 -0
- package/tools/wallet/a2bPublishAgent.ts +0 -307
- package/tools/wallet/sendOrdinals.ts +0 -160
|
@@ -1,156 +1,164 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
type TokenUtxo,
|
|
14
|
-
type TransferOrdTokensConfig,
|
|
15
|
-
transferOrdTokens,
|
|
16
|
-
type Utxo,
|
|
17
|
-
} from "js-1sat-ord";
|
|
18
|
-
import { z } from "zod";
|
|
19
|
-
import { V5Broadcaster } from "../../utils/broadcaster";
|
|
20
|
-
import type { Wallet } from "./wallet";
|
|
1
|
+
import type { OneSatContext } from '@1sat/actions'
|
|
2
|
+
import { sendBsv21, transferOrdinals } from '@1sat/actions'
|
|
3
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
4
|
+
import { z } from 'zod'
|
|
5
|
+
|
|
6
|
+
const walletOutputSchema = z.object({
|
|
7
|
+
outpoint: z.string(),
|
|
8
|
+
satoshis: z.number().optional(),
|
|
9
|
+
tags: z.array(z.string()).optional(),
|
|
10
|
+
customInstructions: z.string().optional(),
|
|
11
|
+
lockingScript: z.string().optional(),
|
|
12
|
+
})
|
|
21
13
|
|
|
22
|
-
// Schema for BSV-20/BSV-21 token transfer arguments
|
|
23
14
|
export const transferOrdTokenArgsSchema = z.object({
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
sendAmount: z.number(),
|
|
27
|
-
paymentUtxos: z.array(
|
|
28
|
-
z.object({
|
|
29
|
-
txid: z.string(),
|
|
30
|
-
vout: z.number(),
|
|
31
|
-
satoshis: z.number(),
|
|
32
|
-
script: z.string(),
|
|
33
|
-
}),
|
|
34
|
-
),
|
|
35
|
-
tokenUtxos: z.array(
|
|
36
|
-
z.object({
|
|
37
|
-
txid: z.string(),
|
|
38
|
-
vout: z.number(),
|
|
39
|
-
satoshis: z.literal(1),
|
|
40
|
-
script: z.string(),
|
|
41
|
-
amt: z.string(),
|
|
42
|
-
id: z.string(),
|
|
43
|
-
}),
|
|
15
|
+
type: z.enum(['ordinal', 'bsv21']).describe(
|
|
16
|
+
"'ordinal' to transfer an inscription/NFT, 'bsv21' to send fungible BSV21 tokens",
|
|
44
17
|
),
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
18
|
+
// ordinal fields
|
|
19
|
+
ordinal: walletOutputSchema
|
|
20
|
+
.optional()
|
|
21
|
+
.describe(
|
|
22
|
+
"WalletOutput of the ordinal to transfer (from wallet_getOrdinals). Required when type='ordinal'",
|
|
23
|
+
),
|
|
24
|
+
inputBEEF: z
|
|
25
|
+
.array(z.number())
|
|
26
|
+
.optional()
|
|
27
|
+
.describe(
|
|
28
|
+
"BEEF bytes from listOutputs (include: 'entire transactions'). Required when type='ordinal'",
|
|
29
|
+
),
|
|
30
|
+
// bsv21 fields
|
|
31
|
+
tokenId: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("Token ID (txid_vout format). Required when type='bsv21'"),
|
|
35
|
+
amount: z
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Amount of tokens to send as a string integer. Required when type='bsv21'"),
|
|
39
|
+
// shared destination fields
|
|
40
|
+
address: z.string().optional().describe('Recipient P2PKH address'),
|
|
41
|
+
counterparty: z
|
|
42
|
+
.string()
|
|
43
|
+
.optional()
|
|
44
|
+
.describe('Recipient identity public key (hex)'),
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
export type TransferOrdTokenArgs = z.infer<typeof transferOrdTokenArgsSchema>
|
|
52
48
|
|
|
53
|
-
/**
|
|
54
|
-
* Register the wallet_transferOrdToken tool for transferring BSV tokens.
|
|
55
|
-
*/
|
|
56
49
|
export function registerTransferOrdTokenTool(
|
|
57
50
|
server: McpServer,
|
|
58
|
-
|
|
51
|
+
ctx: OneSatContext | undefined,
|
|
59
52
|
) {
|
|
60
53
|
server.tool(
|
|
61
|
-
|
|
62
|
-
"
|
|
54
|
+
'wallet_transferOrdToken',
|
|
55
|
+
"Transfer an ordinal inscription or send BSV21 fungible tokens. Use type='ordinal' to transfer an NFT/inscription (requires the WalletOutput from wallet_getOrdinals and the BEEF). Use type='bsv21' to send fungible tokens by token ID and amount.",
|
|
63
56
|
{ ...transferOrdTokenArgsSchema.shape },
|
|
64
|
-
async ({
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
async ({ type, ordinal, inputBEEF, tokenId, amount, address, counterparty }) => {
|
|
58
|
+
if (!ctx) {
|
|
59
|
+
return {
|
|
60
|
+
content: [
|
|
61
|
+
{
|
|
62
|
+
type: 'text',
|
|
63
|
+
text: 'Wallet not initialized. Please configure a wallet before transferring.',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
isError: true,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
70
|
+
try {
|
|
71
|
+
if (type === 'ordinal') {
|
|
72
|
+
if (!ordinal) {
|
|
73
|
+
return {
|
|
74
|
+
content: [{ type: 'text', text: "ordinal is required when type='ordinal'" }],
|
|
75
|
+
isError: true,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!inputBEEF) {
|
|
79
|
+
return {
|
|
80
|
+
content: [{ type: 'text', text: "inputBEEF is required when type='ordinal'" }],
|
|
81
|
+
isError: true,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (!address && !counterparty) {
|
|
85
|
+
return {
|
|
86
|
+
content: [{ type: 'text', text: 'address or counterparty is required' }],
|
|
87
|
+
isError: true,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
83
90
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
paymentPk,
|
|
95
|
-
ordPk,
|
|
96
|
-
additionalPayments: (additionalPayments as Payment[]) || [],
|
|
97
|
-
decimals,
|
|
98
|
-
inputMode: TokenInputMode.Needed,
|
|
99
|
-
splitConfig: {
|
|
100
|
-
outputs: inputTokens.length === 1 ? 2 : 1,
|
|
101
|
-
threshold: sendAmount,
|
|
102
|
-
},
|
|
103
|
-
};
|
|
91
|
+
const result = await transferOrdinals.execute(ctx, {
|
|
92
|
+
transfers: [
|
|
93
|
+
{
|
|
94
|
+
ordinal: ordinal as Parameters<typeof transferOrdinals.execute>[1]['transfers'][0]['ordinal'],
|
|
95
|
+
address,
|
|
96
|
+
counterparty,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
inputBEEF,
|
|
100
|
+
})
|
|
104
101
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
if (result.error) {
|
|
103
|
+
return {
|
|
104
|
+
content: [{ type: 'text', text: result.error }],
|
|
105
|
+
isError: true,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
} as LocalSigner;
|
|
109
|
+
return {
|
|
110
|
+
content: [{ type: 'text', text: JSON.stringify({ txid: result.txid }) }],
|
|
111
|
+
}
|
|
113
112
|
}
|
|
114
113
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
// bsv21
|
|
115
|
+
if (!tokenId) {
|
|
116
|
+
return {
|
|
117
|
+
content: [{ type: 'text', text: "tokenId is required when type='bsv21'" }],
|
|
118
|
+
isError: true,
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!amount) {
|
|
122
|
+
return {
|
|
123
|
+
content: [{ type: 'text', text: "amount is required when type='bsv21'" }],
|
|
124
|
+
isError: true,
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (!address && !counterparty) {
|
|
128
|
+
return {
|
|
129
|
+
content: [{ type: 'text', text: 'address or counterparty is required' }],
|
|
130
|
+
isError: true,
|
|
131
|
+
}
|
|
132
|
+
}
|
|
121
133
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
134
|
+
const result = await sendBsv21.execute(ctx, {
|
|
135
|
+
tokenId,
|
|
136
|
+
amount,
|
|
137
|
+
address,
|
|
138
|
+
counterparty,
|
|
139
|
+
})
|
|
126
140
|
|
|
127
|
-
|
|
141
|
+
if (result.error) {
|
|
128
142
|
return {
|
|
129
|
-
content: [
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
text: JSON.stringify({
|
|
133
|
-
txid: result.tx.id("hex"),
|
|
134
|
-
spentOutpoints: result.spentOutpoints,
|
|
135
|
-
payChange: result.payChange,
|
|
136
|
-
tokenChange: result.tokenChange,
|
|
137
|
-
}),
|
|
138
|
-
},
|
|
139
|
-
],
|
|
140
|
-
};
|
|
143
|
+
content: [{ type: 'text', text: result.error }],
|
|
144
|
+
isError: true,
|
|
145
|
+
}
|
|
141
146
|
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
content: [{ type: 'text', text: JSON.stringify({ txid: result.txid }) }],
|
|
150
|
+
}
|
|
151
|
+
} catch (err: unknown) {
|
|
142
152
|
return {
|
|
143
153
|
content: [
|
|
144
154
|
{
|
|
145
|
-
type:
|
|
146
|
-
text:
|
|
155
|
+
type: 'text',
|
|
156
|
+
text: err instanceof Error ? err.message : String(err),
|
|
147
157
|
},
|
|
148
158
|
],
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
152
|
-
return { content: [{ type: "text", text: msg }], isError: true };
|
|
159
|
+
isError: true,
|
|
160
|
+
}
|
|
153
161
|
}
|
|
154
162
|
},
|
|
155
|
-
)
|
|
163
|
+
)
|
|
156
164
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { OneSatContext } from '@1sat/actions'
|
|
2
|
+
import { unlockBsv } from '@1sat/actions'
|
|
3
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
4
|
+
|
|
5
|
+
export function registerUnlockBsvTool(
|
|
6
|
+
server: McpServer,
|
|
7
|
+
ctx: OneSatContext | undefined,
|
|
8
|
+
) {
|
|
9
|
+
server.tool(
|
|
10
|
+
'wallet_unlockBsv',
|
|
11
|
+
'Unlock all matured time-locked BSV',
|
|
12
|
+
{},
|
|
13
|
+
async () => {
|
|
14
|
+
if (!ctx) {
|
|
15
|
+
return {
|
|
16
|
+
content: [
|
|
17
|
+
{
|
|
18
|
+
type: 'text',
|
|
19
|
+
text: 'Wallet not initialized. Please configure a wallet before unlocking.',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
isError: true,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const result = await unlockBsv.execute(ctx, {})
|
|
28
|
+
|
|
29
|
+
if (result.error) {
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: 'text', text: result.error }],
|
|
32
|
+
isError: true,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: 'text',
|
|
40
|
+
text: JSON.stringify(result, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2),
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
}
|
|
44
|
+
} catch (err: unknown) {
|
|
45
|
+
return {
|
|
46
|
+
content: [
|
|
47
|
+
{
|
|
48
|
+
type: 'text',
|
|
49
|
+
text: err instanceof Error ? err.message : String(err),
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
isError: true,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
}
|
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
import { PrivateKey, Utils } from "@bsv/sdk";
|
|
2
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import type {
|
|
4
|
-
ChangeResult,
|
|
5
|
-
CreateOrdinalsConfig,
|
|
6
|
-
Destination,
|
|
7
|
-
Inscription,
|
|
8
|
-
LocalSigner,
|
|
9
|
-
PreMAP,
|
|
10
|
-
} from "js-1sat-ord";
|
|
11
|
-
import { createOrdinals } from "js-1sat-ord";
|
|
12
|
-
import { z } from "zod";
|
|
13
|
-
import packageJson from "../../package.json";
|
|
14
|
-
import { V5Broadcaster } from "../../utils/broadcaster";
|
|
15
|
-
import type { Wallet } from "./wallet";
|
|
16
|
-
|
|
17
|
-
const { toArray, toBase64 } = Utils;
|
|
18
|
-
|
|
19
|
-
// https://raw.githubusercontent.com/google/A2A/refs/heads/main/specification/json/a2a.json
|
|
20
|
-
|
|
21
|
-
// A2A AgentCard schema (per A2A spec)
|
|
22
|
-
const AgentCapabilitiesSchema = z.object({
|
|
23
|
-
streaming: z.boolean().default(false),
|
|
24
|
-
pushNotifications: z.boolean().default(false),
|
|
25
|
-
stateTransitionHistory: z.boolean().default(false),
|
|
26
|
-
});
|
|
27
|
-
const AgentSkillSchema = z.object({
|
|
28
|
-
id: z.string(),
|
|
29
|
-
name: z.string(),
|
|
30
|
-
description: z.string().nullable(),
|
|
31
|
-
tags: z.array(z.string()).nullable(),
|
|
32
|
-
examples: z.array(z.string()).nullable(),
|
|
33
|
-
inputModes: z.array(z.string()).nullable(),
|
|
34
|
-
outputModes: z.array(z.string()).nullable(),
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// Provider per A2A spec
|
|
38
|
-
const AgentProviderSchema = z
|
|
39
|
-
.object({
|
|
40
|
-
organization: z.string(),
|
|
41
|
-
url: z.string().url().nullable().default(null),
|
|
42
|
-
})
|
|
43
|
-
.nullable()
|
|
44
|
-
.default(null);
|
|
45
|
-
|
|
46
|
-
// Authentication per A2A spec
|
|
47
|
-
const AgentAuthenticationSchema = z
|
|
48
|
-
.object({
|
|
49
|
-
schemes: z.array(z.string()),
|
|
50
|
-
credentials: z.string().nullable().default(null),
|
|
51
|
-
})
|
|
52
|
-
.nullable()
|
|
53
|
-
.default(null);
|
|
54
|
-
|
|
55
|
-
// Pricing plan schema
|
|
56
|
-
export const PricingSchema = z.object({
|
|
57
|
-
id: z.string(),
|
|
58
|
-
description: z.string().nullable().optional(),
|
|
59
|
-
currency: z.string(),
|
|
60
|
-
amount: z
|
|
61
|
-
.number()
|
|
62
|
-
.describe(
|
|
63
|
-
"Cost in standard units of currency (e.g., BSV, USD, not satoshis)",
|
|
64
|
-
),
|
|
65
|
-
address: z.string(),
|
|
66
|
-
acceptedCurrencies: z.array(z.string()).optional(),
|
|
67
|
-
skills: z.array(z.string()).optional(),
|
|
68
|
-
interval: z.enum(["day", "week", "month", "year"]).nullable().optional(),
|
|
69
|
-
includedCalls: z.record(z.number()).optional(),
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
export type PricingConfig = z.infer<typeof PricingSchema>;
|
|
73
|
-
|
|
74
|
-
export const AgentCardSchema = z.object({
|
|
75
|
-
name: z.string(),
|
|
76
|
-
description: z.string().nullable().default(null),
|
|
77
|
-
url: z.string().url(),
|
|
78
|
-
provider: AgentProviderSchema,
|
|
79
|
-
version: z.string(),
|
|
80
|
-
documentationUrl: z.string().url().nullable().default(null),
|
|
81
|
-
capabilities: AgentCapabilitiesSchema,
|
|
82
|
-
authentication: AgentAuthenticationSchema,
|
|
83
|
-
defaultInputModes: z.array(z.string()).default(["text"]),
|
|
84
|
-
defaultOutputModes: z.array(z.string()).default(["text"]),
|
|
85
|
-
skills: z.array(AgentSkillSchema),
|
|
86
|
-
"x-payment": z.array(PricingSchema).optional().default([]),
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Schema for on-chain agent publish parameters
|
|
90
|
-
export const a2bPublishArgsSchema = z.object({
|
|
91
|
-
agentUrl: z
|
|
92
|
-
.string()
|
|
93
|
-
.url()
|
|
94
|
-
.describe("Agent base URL (e.g. https://example.com)"),
|
|
95
|
-
agentName: z.string().describe("Human-friendly agent name"),
|
|
96
|
-
description: z
|
|
97
|
-
.string()
|
|
98
|
-
.nullable()
|
|
99
|
-
.optional()
|
|
100
|
-
.describe("Optional agent description"),
|
|
101
|
-
providerOrganization: z
|
|
102
|
-
.string()
|
|
103
|
-
.optional()
|
|
104
|
-
.describe("Optional provider organization name"),
|
|
105
|
-
providerUrl: z.string().url().optional().describe("Optional provider URL"),
|
|
106
|
-
version: z.string().optional().describe("Optional agent version"),
|
|
107
|
-
documentationUrl: z
|
|
108
|
-
.string()
|
|
109
|
-
.url()
|
|
110
|
-
.nullable()
|
|
111
|
-
.optional()
|
|
112
|
-
.describe("Optional documentation URL"),
|
|
113
|
-
streaming: z
|
|
114
|
-
.boolean()
|
|
115
|
-
.default(false)
|
|
116
|
-
.describe("Supports SSE (tasks/sendSubscribe)"),
|
|
117
|
-
pushNotifications: z
|
|
118
|
-
.boolean()
|
|
119
|
-
.default(false)
|
|
120
|
-
.describe("Supports push notifications"),
|
|
121
|
-
stateTransitionHistory: z
|
|
122
|
-
.boolean()
|
|
123
|
-
.default(false)
|
|
124
|
-
.describe("Supports state transition history"),
|
|
125
|
-
defaultInputModes: z
|
|
126
|
-
.array(z.string())
|
|
127
|
-
.default(["text"])
|
|
128
|
-
.describe("Default input modes"),
|
|
129
|
-
defaultOutputModes: z
|
|
130
|
-
.array(z.string())
|
|
131
|
-
.default(["text"])
|
|
132
|
-
.describe("Default output modes"),
|
|
133
|
-
skills: z
|
|
134
|
-
.array(AgentSkillSchema)
|
|
135
|
-
.optional()
|
|
136
|
-
.default([])
|
|
137
|
-
.describe("List of agent skills"),
|
|
138
|
-
destinationAddress: z
|
|
139
|
-
.string()
|
|
140
|
-
.optional()
|
|
141
|
-
.describe("Optional target address for inscription"),
|
|
142
|
-
});
|
|
143
|
-
export type A2bPublishArgs = z.infer<typeof a2bPublishArgsSchema>;
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Registers the wallet_a2bPublish tool for publishing an agent record on-chain
|
|
147
|
-
*/
|
|
148
|
-
export function registerA2bPublishAgentTool(server: McpServer, wallet: Wallet) {
|
|
149
|
-
server.tool(
|
|
150
|
-
"wallet_a2bPublish",
|
|
151
|
-
"Publish an agent.json record on-chain via Ordinal inscription",
|
|
152
|
-
{ ...a2bPublishArgsSchema.shape },
|
|
153
|
-
async ({ agentUrl, agentName, description, providerOrganization, providerUrl, version, documentationUrl, streaming, pushNotifications, stateTransitionHistory, defaultInputModes, defaultOutputModes, skills, destinationAddress }) => {
|
|
154
|
-
try {
|
|
155
|
-
const paymentPk = wallet.getPaymentKey();
|
|
156
|
-
if (!paymentPk) throw new Error("No private key available");
|
|
157
|
-
const { paymentUtxos } = await wallet.getUtxos();
|
|
158
|
-
if (!paymentUtxos?.length)
|
|
159
|
-
throw new Error("No payment UTXOs available to fund inscription");
|
|
160
|
-
|
|
161
|
-
// TODO: Get the skills from actually running the MCP instead of trusting the agent args for skills
|
|
162
|
-
const walletAddress = paymentPk.toAddress().toString();
|
|
163
|
-
|
|
164
|
-
// Create pricing plans using the new schema
|
|
165
|
-
const pricingConfig: PricingConfig[] = [
|
|
166
|
-
// {
|
|
167
|
-
// id: "subscription-premium",
|
|
168
|
-
// description: "Premium subscription with all features",
|
|
169
|
-
// currency: "USD",
|
|
170
|
-
// amount: 10,
|
|
171
|
-
// address: walletAddress,
|
|
172
|
-
// interval: "month",
|
|
173
|
-
// skills: ["wallet_a2bPublish", "wallet_a2bCall"],
|
|
174
|
-
// },
|
|
175
|
-
// {
|
|
176
|
-
// id: "subscription-free",
|
|
177
|
-
// description: "Free tier with limited features",
|
|
178
|
-
// currency: "USD",
|
|
179
|
-
// amount: 0,
|
|
180
|
-
// address: walletAddress,
|
|
181
|
-
// interval: "month",
|
|
182
|
-
// skills: ["wallet_a2bCall"],
|
|
183
|
-
// },
|
|
184
|
-
{
|
|
185
|
-
id: "pay-per-call-default",
|
|
186
|
-
description: "Pay-per-call for publish operations",
|
|
187
|
-
currency: "USD",
|
|
188
|
-
amount: 1,
|
|
189
|
-
address: walletAddress,
|
|
190
|
-
skills: ["wallet_a2bPublish"],
|
|
191
|
-
interval: null,
|
|
192
|
-
},
|
|
193
|
-
// {
|
|
194
|
-
// id: "pay-per-call-royalty",
|
|
195
|
-
// description: "Royalty payment for agent",
|
|
196
|
-
// currency: "USD",
|
|
197
|
-
// amount: 1,
|
|
198
|
-
// address: "1JOExxxxxxxxxxxx",
|
|
199
|
-
// skills: ["wallet_a2bPublish"],
|
|
200
|
-
// interval: null,
|
|
201
|
-
// }
|
|
202
|
-
];
|
|
203
|
-
|
|
204
|
-
// Assemble AgentCard with defaults and user overrides
|
|
205
|
-
const agentCard = {
|
|
206
|
-
name: agentName,
|
|
207
|
-
description: description ?? null,
|
|
208
|
-
url: agentUrl,
|
|
209
|
-
provider:
|
|
210
|
-
providerOrganization && providerUrl
|
|
211
|
-
? {
|
|
212
|
-
organization: providerOrganization,
|
|
213
|
-
url: providerUrl,
|
|
214
|
-
}
|
|
215
|
-
: null,
|
|
216
|
-
version: version ?? packageJson.version,
|
|
217
|
-
documentationUrl: documentationUrl ?? null,
|
|
218
|
-
capabilities: {
|
|
219
|
-
streaming,
|
|
220
|
-
pushNotifications,
|
|
221
|
-
stateTransitionHistory,
|
|
222
|
-
},
|
|
223
|
-
authentication: null,
|
|
224
|
-
defaultInputModes,
|
|
225
|
-
defaultOutputModes,
|
|
226
|
-
skills,
|
|
227
|
-
"x-payment-config": pricingConfig,
|
|
228
|
-
};
|
|
229
|
-
// Validate compliance
|
|
230
|
-
AgentCardSchema.parse(agentCard);
|
|
231
|
-
const fileContent = JSON.stringify(agentCard, null, 2);
|
|
232
|
-
// Base64 payload for inscription
|
|
233
|
-
const dataB64 = toBase64(toArray(fileContent));
|
|
234
|
-
const inscription: Inscription = {
|
|
235
|
-
dataB64,
|
|
236
|
-
contentType: "application/json",
|
|
237
|
-
};
|
|
238
|
-
// Destination for the ordinal
|
|
239
|
-
const targetAddress = destinationAddress ?? walletAddress;
|
|
240
|
-
const destinations: Destination[] = [
|
|
241
|
-
{ address: targetAddress, inscription },
|
|
242
|
-
];
|
|
243
|
-
// Default MAP metadata: file path, content type, encoding
|
|
244
|
-
const metaData: PreMAP = { app: "bsv-mcp", type: "a2b" };
|
|
245
|
-
|
|
246
|
-
const createOrdinalsConfig: CreateOrdinalsConfig = {
|
|
247
|
-
utxos: paymentUtxos,
|
|
248
|
-
destinations,
|
|
249
|
-
paymentPk,
|
|
250
|
-
changeAddress: walletAddress,
|
|
251
|
-
metaData,
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
const identityPk = process.env.IDENTITY_KEY_WIF
|
|
255
|
-
? PrivateKey.fromWif(process.env.IDENTITY_KEY_WIF)
|
|
256
|
-
: undefined;
|
|
257
|
-
if (identityPk) {
|
|
258
|
-
createOrdinalsConfig.signer = {
|
|
259
|
-
idKey: identityPk,
|
|
260
|
-
} as LocalSigner;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Inscribe the ordinal on-chain via js-1sat-ord
|
|
264
|
-
const result = await createOrdinals(createOrdinalsConfig);
|
|
265
|
-
const changeResult = result as ChangeResult;
|
|
266
|
-
|
|
267
|
-
const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
|
|
268
|
-
if (!disableBroadcasting) {
|
|
269
|
-
const broadcaster = new V5Broadcaster();
|
|
270
|
-
await changeResult.tx.broadcast(broadcaster);
|
|
271
|
-
|
|
272
|
-
// Refresh UTXOs
|
|
273
|
-
try {
|
|
274
|
-
await wallet.refreshUtxos();
|
|
275
|
-
} catch {}
|
|
276
|
-
// Return transaction details
|
|
277
|
-
return {
|
|
278
|
-
content: [
|
|
279
|
-
{
|
|
280
|
-
type: "text",
|
|
281
|
-
text: JSON.stringify({
|
|
282
|
-
txid: changeResult.tx.id("hex"),
|
|
283
|
-
spentOutpoints: changeResult.spentOutpoints,
|
|
284
|
-
payChange: changeResult.payChange,
|
|
285
|
-
inscriptionAddress: targetAddress,
|
|
286
|
-
agentCard,
|
|
287
|
-
}),
|
|
288
|
-
},
|
|
289
|
-
],
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return {
|
|
294
|
-
content: [
|
|
295
|
-
{
|
|
296
|
-
type: "text",
|
|
297
|
-
text: changeResult.tx.toHex(),
|
|
298
|
-
},
|
|
299
|
-
],
|
|
300
|
-
};
|
|
301
|
-
} catch (err: unknown) {
|
|
302
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
303
|
-
return { content: [{ type: "text", text: msg }], isError: true };
|
|
304
|
-
}
|
|
305
|
-
},
|
|
306
|
-
);
|
|
307
|
-
}
|