bsv-mcp 0.0.35 → 0.2.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 +112 -1
- package/README.md +233 -4
- package/dist/index.js +111762 -0
- package/index.ts +804 -86
- package/next-env.d.ts +6 -0
- package/package.json +57 -15
- package/prompts/bsvSdk/index.ts +5 -5
- package/prompts/bsvSdk/transaction.ts +2 -2
- package/prompts/bsvSdk/wallet.ts +1 -1
- package/prompts/index.ts +1 -1
- package/resources/bitcom.ts +132 -0
- package/resources/resources.ts +7 -3
- package/smithery.yaml +3 -0
- package/test-mcp-server.ts +157 -0
- package/tools/bap/friend.ts +267 -0
- package/tools/bap/generate.test.ts +350 -0
- package/tools/bap/generate.ts +433 -0
- package/tools/bap/getCurrentAddress.ts +76 -0
- package/tools/bap/getId.test.ts +121 -0
- package/tools/bap/getId.ts +169 -0
- package/tools/bap/index.ts +111 -0
- package/tools/bap/types.ts +66 -0
- package/tools/bap/utils.ts +33 -0
- package/tools/bigblocks/components.ts +304 -0
- package/tools/bigblocks/docs.ts +488 -0
- package/tools/bigblocks/examples.ts +527 -0
- package/tools/bigblocks/generator.ts +485 -0
- package/tools/bigblocks/index.ts +23 -0
- package/tools/bsocial/bigblocksApiClient.ts +189 -0
- package/tools/bsocial/bmapFollow.ts +125 -0
- package/tools/bsocial/bmapLikes.ts +111 -0
- package/tools/bsocial/bmapReadPosts.ts +148 -0
- package/tools/bsocial/createPost.ts +136 -0
- package/tools/bsocial/index.ts +45 -0
- package/tools/bsocial/readPosts.ts +255 -0
- package/tools/bsv/decodeTransaction.ts +5 -1
- package/tools/constants.ts +21 -0
- package/tools/index.ts +103 -17
- package/tools/mnee/index.ts +8 -0
- package/tools/mnee/parseTx.ts +1 -1
- package/tools/mnee/sendMnee.ts +1 -1
- package/tools/utils/aip.ts +46 -0
- package/tools/utils/conversion.ts +1 -0
- package/tools/utils/errorHandler.ts +76 -0
- package/tools/utils/index.ts +45 -1
- package/tools/utils/installAgentMaster.ts +84 -0
- package/tools/utils/logger.ts +20 -0
- package/tools/utils/toolConfig.ts +80 -0
- package/tools/utils/toolRegistration.ts +66 -0
- package/tools/utils/transactionBuilder.ts +196 -0
- package/tools/wallet/a2bPublishAgent.ts +8 -4
- package/tools/wallet/a2bPublishMcp.ts +28 -24
- package/tools/wallet/createOrdinals.ts +4 -2
- package/tools/wallet/fetchPaymentUtxos.ts +44 -43
- package/tools/wallet/gatherCollectionInfo.ts +374 -0
- package/tools/wallet/getAddress.ts +13 -21
- package/tools/wallet/getBalance.ts +66 -0
- package/tools/wallet/getBalanceDroplet.ts +59 -0
- package/tools/wallet/integratedWallet.ts +120 -0
- package/tools/wallet/mintCollection.ts +378 -0
- package/tools/wallet/purchaseListing.ts +4 -4
- package/tools/wallet/schemas.ts +18 -249
- package/tools/wallet/sendOrdinals.ts +4 -2
- package/tools/wallet/sendToAddress.ts +5 -1
- package/tools/wallet/setupDroplet.ts +172 -0
- package/tools/wallet/tools.ts +71 -156
- package/tools/wallet/transactionHelper.ts +121 -0
- package/tools/wallet/transferOrdToken.ts +7 -4
- package/tools/wallet/utxo.ts +62 -0
- package/tools/wallet/wallet.ts +237 -234
- package/tools/a2b/call.ts +0 -32
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { BMAP_URL } from "../constants";
|
|
5
|
+
|
|
6
|
+
// Schema for reading follows from BMAP API
|
|
7
|
+
const bmapReadFollowsArgsSchema = z.object({
|
|
8
|
+
bapId: z
|
|
9
|
+
.string()
|
|
10
|
+
.describe("BAP identity key to get follow relationships for"),
|
|
11
|
+
type: z
|
|
12
|
+
.enum(["followers", "following"])
|
|
13
|
+
.default("following")
|
|
14
|
+
.describe("Type of relationship to query"),
|
|
15
|
+
limit: z
|
|
16
|
+
.number()
|
|
17
|
+
.min(1)
|
|
18
|
+
.max(100)
|
|
19
|
+
.default(20)
|
|
20
|
+
.describe("Maximum number of follows to return"),
|
|
21
|
+
page: z
|
|
22
|
+
.number()
|
|
23
|
+
.min(1)
|
|
24
|
+
.default(1)
|
|
25
|
+
.describe("Page number for pagination (1-based)"),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
type BmapReadFollowsArgs = z.infer<typeof bmapReadFollowsArgsSchema>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Read follow relationships from BMAP API
|
|
32
|
+
*/
|
|
33
|
+
export async function readBmapFollows(args: BmapReadFollowsArgs): Promise<{
|
|
34
|
+
success: boolean;
|
|
35
|
+
data?: unknown;
|
|
36
|
+
error?: string;
|
|
37
|
+
}> {
|
|
38
|
+
try {
|
|
39
|
+
const { bapId, type, limit, page } = args;
|
|
40
|
+
|
|
41
|
+
const params = new URLSearchParams();
|
|
42
|
+
params.append("limit", limit.toString());
|
|
43
|
+
params.append("page", page.toString());
|
|
44
|
+
|
|
45
|
+
const endpoint = type === "followers" ? "followers" : "following";
|
|
46
|
+
const response = await fetch(
|
|
47
|
+
`${BMAP_URL}/bap/${bapId}/${endpoint}?${params}`,
|
|
48
|
+
{
|
|
49
|
+
method: "GET",
|
|
50
|
+
headers: {
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
const errorText = await response.text();
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
error: `BMAP API request failed: ${response.status} ${errorText}`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const data = await response.json();
|
|
65
|
+
return {
|
|
66
|
+
success: true,
|
|
67
|
+
data,
|
|
68
|
+
};
|
|
69
|
+
} catch (error) {
|
|
70
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
71
|
+
console.error("Error reading BMAP follows:", errorMessage);
|
|
72
|
+
return {
|
|
73
|
+
success: false,
|
|
74
|
+
error: errorMessage,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Register the BMAP read follows tool with the MCP server
|
|
81
|
+
*/
|
|
82
|
+
export function registerBmapReadFollowsTool(server: McpServer) {
|
|
83
|
+
server.tool(
|
|
84
|
+
"bmap_readFollows",
|
|
85
|
+
"Read follow relationships from the BMAP API. Shows who a user is following or who follows them.",
|
|
86
|
+
{ args: bmapReadFollowsArgsSchema },
|
|
87
|
+
async ({
|
|
88
|
+
args,
|
|
89
|
+
}: {
|
|
90
|
+
args: BmapReadFollowsArgs;
|
|
91
|
+
}): Promise<CallToolResult> => {
|
|
92
|
+
try {
|
|
93
|
+
const result = await readBmapFollows(args);
|
|
94
|
+
|
|
95
|
+
if (result.success) {
|
|
96
|
+
return {
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: "text",
|
|
100
|
+
text: JSON.stringify(result.data, null, 2),
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
isError: false,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
content: [
|
|
109
|
+
{
|
|
110
|
+
type: "text",
|
|
111
|
+
text: result.error || "Unknown error occurred",
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
isError: true,
|
|
115
|
+
};
|
|
116
|
+
} catch (error) {
|
|
117
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
118
|
+
return {
|
|
119
|
+
content: [{ type: "text", text: `Error: ${msg}` }],
|
|
120
|
+
isError: true,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { BMAP_URL } from "../constants";
|
|
5
|
+
|
|
6
|
+
// Schema for reading likes from BMAP API
|
|
7
|
+
const bmapReadLikesArgsSchema = z.object({
|
|
8
|
+
txid: z.string().describe("Transaction ID of the post to get likes for"),
|
|
9
|
+
limit: z
|
|
10
|
+
.number()
|
|
11
|
+
.min(1)
|
|
12
|
+
.max(100)
|
|
13
|
+
.default(20)
|
|
14
|
+
.describe("Maximum number of likes to return"),
|
|
15
|
+
page: z
|
|
16
|
+
.number()
|
|
17
|
+
.min(1)
|
|
18
|
+
.default(1)
|
|
19
|
+
.describe("Page number for pagination (1-based)"),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
type BmapReadLikesArgs = z.infer<typeof bmapReadLikesArgsSchema>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Read likes and reactions for a specific post from BMAP API
|
|
26
|
+
*/
|
|
27
|
+
export async function readBmapLikes(args: BmapReadLikesArgs): Promise<{
|
|
28
|
+
success: boolean;
|
|
29
|
+
data?: unknown;
|
|
30
|
+
error?: string;
|
|
31
|
+
}> {
|
|
32
|
+
try {
|
|
33
|
+
const { txid, limit, page } = args;
|
|
34
|
+
|
|
35
|
+
const params = new URLSearchParams();
|
|
36
|
+
params.append("limit", limit.toString());
|
|
37
|
+
params.append("page", page.toString());
|
|
38
|
+
|
|
39
|
+
const response = await fetch(`${BMAP_URL}/post/${txid}/like?${params}`, {
|
|
40
|
+
method: "GET",
|
|
41
|
+
headers: {
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
const errorText = await response.text();
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
error: `BMAP API request failed: ${response.status} ${errorText}`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const data = await response.json();
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
data,
|
|
58
|
+
};
|
|
59
|
+
} catch (error) {
|
|
60
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
61
|
+
console.error("Error reading BMAP likes:", errorMessage);
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: errorMessage,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Register the BMAP read likes tool with the MCP server
|
|
71
|
+
*/
|
|
72
|
+
export function registerBmapReadLikesTool(server: McpServer) {
|
|
73
|
+
server.tool(
|
|
74
|
+
"bmap_readLikes",
|
|
75
|
+
"Read likes and reactions for a specific post from the BMAP API. Shows who liked the post and what emoji reactions were used.",
|
|
76
|
+
{ args: bmapReadLikesArgsSchema },
|
|
77
|
+
async ({ args }: { args: BmapReadLikesArgs }): Promise<CallToolResult> => {
|
|
78
|
+
try {
|
|
79
|
+
const result = await readBmapLikes(args);
|
|
80
|
+
|
|
81
|
+
if (result.success) {
|
|
82
|
+
return {
|
|
83
|
+
content: [
|
|
84
|
+
{
|
|
85
|
+
type: "text",
|
|
86
|
+
text: JSON.stringify(result.data, null, 2),
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
isError: false,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
content: [
|
|
95
|
+
{
|
|
96
|
+
type: "text",
|
|
97
|
+
text: result.error || "Unknown error occurred",
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
isError: true,
|
|
101
|
+
};
|
|
102
|
+
} catch (error) {
|
|
103
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
104
|
+
return {
|
|
105
|
+
content: [{ type: "text", text: `Error: ${msg}` }],
|
|
106
|
+
isError: true,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { BMAP_URL } from "../constants";
|
|
5
|
+
|
|
6
|
+
// Schema for reading posts from BMAP API
|
|
7
|
+
const bmapReadPostsArgsSchema = z.object({
|
|
8
|
+
bapId: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("BAP identity key to filter posts by author"),
|
|
12
|
+
txid: z.string().optional().describe("Specific transaction ID to fetch"),
|
|
13
|
+
limit: z
|
|
14
|
+
.number()
|
|
15
|
+
.min(1)
|
|
16
|
+
.max(100)
|
|
17
|
+
.default(20)
|
|
18
|
+
.describe("Maximum number of posts to return"),
|
|
19
|
+
page: z
|
|
20
|
+
.number()
|
|
21
|
+
.min(1)
|
|
22
|
+
.default(1)
|
|
23
|
+
.describe("Page number for pagination (1-based)"),
|
|
24
|
+
feed: z
|
|
25
|
+
.boolean()
|
|
26
|
+
.default(false)
|
|
27
|
+
.describe("Whether to fetch feed (posts from followed users)"),
|
|
28
|
+
address: z.string().optional().describe("Bitcoin address to filter posts by"),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
type BmapReadPostsArgs = z.infer<typeof bmapReadPostsArgsSchema>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Read social posts from the BMAP API (the query/read layer)
|
|
35
|
+
*/
|
|
36
|
+
export async function readBmapPosts(args: BmapReadPostsArgs): Promise<{
|
|
37
|
+
success: boolean;
|
|
38
|
+
data?: unknown;
|
|
39
|
+
error?: string;
|
|
40
|
+
}> {
|
|
41
|
+
try {
|
|
42
|
+
const { bapId, txid, limit, page, feed, address } = args;
|
|
43
|
+
|
|
44
|
+
// Handle single post request
|
|
45
|
+
if (txid) {
|
|
46
|
+
const response = await fetch(`${BMAP_URL}/post/${txid}`, {
|
|
47
|
+
method: "GET",
|
|
48
|
+
headers: {
|
|
49
|
+
"Content-Type": "application/json",
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const errorText = await response.text();
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
error: `BMAP API request failed: ${response.status} ${errorText}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const data = await response.json();
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
data,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Handle posts list request
|
|
69
|
+
const params = new URLSearchParams();
|
|
70
|
+
if (bapId) params.append("bapId", bapId);
|
|
71
|
+
if (address) params.append("address", address);
|
|
72
|
+
params.append("limit", limit.toString());
|
|
73
|
+
params.append("page", page.toString());
|
|
74
|
+
if (feed) params.append("feed", "true");
|
|
75
|
+
|
|
76
|
+
const response = await fetch(`${BMAP_URL}/posts?${params}`, {
|
|
77
|
+
method: "GET",
|
|
78
|
+
headers: {
|
|
79
|
+
"Content-Type": "application/json",
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
const errorText = await response.text();
|
|
85
|
+
return {
|
|
86
|
+
success: false,
|
|
87
|
+
error: `BMAP API request failed: ${response.status} ${errorText}`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const data = await response.json();
|
|
92
|
+
return {
|
|
93
|
+
success: true,
|
|
94
|
+
data,
|
|
95
|
+
};
|
|
96
|
+
} catch (error) {
|
|
97
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
98
|
+
console.error("Error reading BMAP posts:", errorMessage);
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
error: errorMessage,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Register the BMAP read posts tool with the MCP server
|
|
108
|
+
*/
|
|
109
|
+
export function registerBmapReadPostsTool(server: McpServer) {
|
|
110
|
+
server.tool(
|
|
111
|
+
"bmap_readPosts",
|
|
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
|
+
{ args: bmapReadPostsArgsSchema },
|
|
114
|
+
async ({ args }: { args: BmapReadPostsArgs }): Promise<CallToolResult> => {
|
|
115
|
+
try {
|
|
116
|
+
const result = await readBmapPosts(args);
|
|
117
|
+
|
|
118
|
+
if (result.success) {
|
|
119
|
+
return {
|
|
120
|
+
content: [
|
|
121
|
+
{
|
|
122
|
+
type: "text",
|
|
123
|
+
text: JSON.stringify(result.data, null, 2),
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
isError: false,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
content: [
|
|
132
|
+
{
|
|
133
|
+
type: "text",
|
|
134
|
+
text: result.error || "Unknown error occurred",
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
isError: true,
|
|
138
|
+
};
|
|
139
|
+
} catch (error) {
|
|
140
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
141
|
+
return {
|
|
142
|
+
content: [{ type: "text", text: `Error: ${msg}` }],
|
|
143
|
+
isError: true,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Utils } from "@bsv/sdk";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { B_PREFIX, MAP_PREFIX } from "../constants";
|
|
6
|
+
import { signOpReturnWithAIP } from "../utils/aip";
|
|
7
|
+
import { createResponse, formatError } from "../utils/errorHandler";
|
|
8
|
+
import {
|
|
9
|
+
buildAndSendTransaction,
|
|
10
|
+
buildOpReturnScript,
|
|
11
|
+
} from "../utils/transactionBuilder";
|
|
12
|
+
import type { Wallet } from "../wallet/wallet";
|
|
13
|
+
|
|
14
|
+
const { toArray } = Utils;
|
|
15
|
+
|
|
16
|
+
// Schema for creating a social post
|
|
17
|
+
const createPostArgsSchema = z.object({
|
|
18
|
+
content: z.string().describe("The content of the post"),
|
|
19
|
+
contentType: z
|
|
20
|
+
.enum(["text/plain", "text/markdown"])
|
|
21
|
+
.default("text/plain")
|
|
22
|
+
.describe("Content type of the post"),
|
|
23
|
+
app: z
|
|
24
|
+
.string()
|
|
25
|
+
.default("bsv-mcp")
|
|
26
|
+
.describe("Application name creating the post"),
|
|
27
|
+
additionalMapData: z
|
|
28
|
+
.record(z.string())
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("Additional MAP protocol key-value pairs"),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
type CreatePostArgs = z.infer<typeof createPostArgsSchema>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create a social post on the BSV blockchain using B:// and MAP protocols
|
|
37
|
+
*/
|
|
38
|
+
export async function createSocialPost(
|
|
39
|
+
args: CreatePostArgs,
|
|
40
|
+
wallet: Wallet,
|
|
41
|
+
): Promise<{
|
|
42
|
+
success: boolean;
|
|
43
|
+
txid?: string;
|
|
44
|
+
rawTx?: string;
|
|
45
|
+
error?: string;
|
|
46
|
+
}> {
|
|
47
|
+
try {
|
|
48
|
+
const { content, contentType, app, additionalMapData } = args;
|
|
49
|
+
|
|
50
|
+
// Get wallet info
|
|
51
|
+
const address = await wallet.getAddress();
|
|
52
|
+
const paymentKey = wallet.getPaymentKey();
|
|
53
|
+
if (!paymentKey) {
|
|
54
|
+
return {
|
|
55
|
+
success: false,
|
|
56
|
+
error: "Payment key not available in wallet",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Prepare file extension based on content type
|
|
61
|
+
const fileExtension = contentType === "text/markdown" ? "md" : "txt";
|
|
62
|
+
const fileName = `post.${fileExtension}`;
|
|
63
|
+
|
|
64
|
+
// Build B protocol data
|
|
65
|
+
const bData = [
|
|
66
|
+
toArray(B_PREFIX, "utf8"),
|
|
67
|
+
toArray(content, "utf8"),
|
|
68
|
+
toArray(contentType, "utf8"),
|
|
69
|
+
toArray("utf-8", "utf8"),
|
|
70
|
+
toArray(fileName, "utf8"),
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
// Build MAP protocol data
|
|
74
|
+
const mapData: Uint8Array[] = [
|
|
75
|
+
toArray(MAP_PREFIX, "utf8"),
|
|
76
|
+
toArray("SET", "utf8"),
|
|
77
|
+
toArray("app", "utf8"),
|
|
78
|
+
toArray(app, "utf8"),
|
|
79
|
+
toArray("type", "utf8"),
|
|
80
|
+
toArray("post", "utf8"),
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
// Add any additional MAP data
|
|
84
|
+
if (additionalMapData) {
|
|
85
|
+
for (const [key, value] of Object.entries(additionalMapData)) {
|
|
86
|
+
mapData.push(toArray(key, "utf8"), toArray(value, "utf8"));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Combine protocols with pipe separator
|
|
91
|
+
const pipeData = toArray("|", "utf8");
|
|
92
|
+
const dataToSign = [...bData, pipeData, ...mapData];
|
|
93
|
+
|
|
94
|
+
// Sign the data with AIP
|
|
95
|
+
const { signedData } = await signOpReturnWithAIP(
|
|
96
|
+
dataToSign,
|
|
97
|
+
paymentKey,
|
|
98
|
+
address.toString(),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Create OP_RETURN script
|
|
102
|
+
const script = buildOpReturnScript(signedData);
|
|
103
|
+
|
|
104
|
+
// Get UTXOs
|
|
105
|
+
const utxos = await wallet.getPaymentUtxos();
|
|
106
|
+
|
|
107
|
+
// Build and send transaction using the new utility
|
|
108
|
+
return await buildAndSendTransaction({
|
|
109
|
+
outputs: [{ script, satoshis: 0 }],
|
|
110
|
+
utxos,
|
|
111
|
+
changeAddress: address.toString(),
|
|
112
|
+
paymentKey,
|
|
113
|
+
});
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error("Error creating social post:", formatError(error));
|
|
116
|
+
return {
|
|
117
|
+
success: false,
|
|
118
|
+
error: formatError(error),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Register the social post tool with the MCP server
|
|
125
|
+
*/
|
|
126
|
+
export function registerCreatePostTool(server: McpServer, wallet: Wallet) {
|
|
127
|
+
server.tool(
|
|
128
|
+
"bsocial_createPost",
|
|
129
|
+
"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.",
|
|
130
|
+
{ args: createPostArgsSchema },
|
|
131
|
+
async ({ args }: { args: CreatePostArgs }): Promise<CallToolResult> => {
|
|
132
|
+
const result = await createSocialPost(args, wallet);
|
|
133
|
+
return createResponse(result);
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { Wallet } from "../wallet/wallet";
|
|
3
|
+
import { registerBmapReadFollowsTool } from "./bmapFollow";
|
|
4
|
+
import { registerBmapReadLikesTool } from "./bmapLikes";
|
|
5
|
+
import { registerBmapReadPostsTool } from "./bmapReadPosts";
|
|
6
|
+
import { registerCreatePostTool } from "./createPost";
|
|
7
|
+
import { registerReadPostsTool } from "./readPosts";
|
|
8
|
+
|
|
9
|
+
interface BsocialToolsConfig {
|
|
10
|
+
wallet?: Wallet;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Register all BSocial tools with the MCP server
|
|
15
|
+
* @param server The MCP server instance
|
|
16
|
+
* @param config Configuration including wallet instance
|
|
17
|
+
*/
|
|
18
|
+
export function registerBsocialTools(
|
|
19
|
+
server: McpServer,
|
|
20
|
+
config: BsocialToolsConfig,
|
|
21
|
+
): void {
|
|
22
|
+
// Register tools that don't require wallet
|
|
23
|
+
registerReadPostsTool(server);
|
|
24
|
+
console.error("✅ Registered bsocial_readPosts tool");
|
|
25
|
+
|
|
26
|
+
// Register BMAP API query tools (read layer)
|
|
27
|
+
registerBmapReadPostsTool(server);
|
|
28
|
+
console.error("✅ Registered bmap_readPosts tool");
|
|
29
|
+
|
|
30
|
+
registerBmapReadLikesTool(server);
|
|
31
|
+
console.error("✅ Registered bmap_readLikes tool");
|
|
32
|
+
|
|
33
|
+
registerBmapReadFollowsTool(server);
|
|
34
|
+
console.error("✅ Registered bmap_readFollows tool");
|
|
35
|
+
|
|
36
|
+
// Register tools that require wallet
|
|
37
|
+
if (config.wallet) {
|
|
38
|
+
registerCreatePostTool(server, config.wallet);
|
|
39
|
+
console.error("✅ Registered bsocial_createPost tool");
|
|
40
|
+
} else {
|
|
41
|
+
console.error(
|
|
42
|
+
"⚠️ bsocial_createPost tool not registered (no wallet available)",
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|