@buzzposter/mcp 0.1.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/dist/index.d.ts +2 -0
- package/dist/index.js +805 -0
- package/package.json +38 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,805 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
|
|
7
|
+
// src/client.ts
|
|
8
|
+
var BuzzPosterClient = class {
|
|
9
|
+
baseUrl;
|
|
10
|
+
apiKey;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
13
|
+
this.apiKey = config.apiKey;
|
|
14
|
+
}
|
|
15
|
+
async request(method, path, body, query) {
|
|
16
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
17
|
+
if (query) {
|
|
18
|
+
for (const [k, v] of Object.entries(query)) {
|
|
19
|
+
if (v !== void 0 && v !== "") url.searchParams.set(k, v);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const headers = {
|
|
23
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
24
|
+
};
|
|
25
|
+
if (body) {
|
|
26
|
+
headers["Content-Type"] = "application/json";
|
|
27
|
+
}
|
|
28
|
+
const res = await fetch(url, {
|
|
29
|
+
method,
|
|
30
|
+
headers,
|
|
31
|
+
body: body ? JSON.stringify(body) : void 0
|
|
32
|
+
});
|
|
33
|
+
if (!res.ok) {
|
|
34
|
+
const errorBody = await res.json().catch(() => ({ message: res.statusText }));
|
|
35
|
+
const message = typeof errorBody === "object" && errorBody !== null && "message" in errorBody ? String(errorBody.message) : `API error (${res.status})`;
|
|
36
|
+
throw new Error(`BuzzPoster API error (${res.status}): ${message}`);
|
|
37
|
+
}
|
|
38
|
+
const text = await res.text();
|
|
39
|
+
return text ? JSON.parse(text) : void 0;
|
|
40
|
+
}
|
|
41
|
+
// Account
|
|
42
|
+
async getAccount() {
|
|
43
|
+
return this.request("GET", "/api/v1/account");
|
|
44
|
+
}
|
|
45
|
+
// Social accounts
|
|
46
|
+
async listAccounts() {
|
|
47
|
+
return this.request("GET", "/api/v1/accounts");
|
|
48
|
+
}
|
|
49
|
+
async checkAccountsHealth() {
|
|
50
|
+
return this.request("GET", "/api/v1/accounts/health");
|
|
51
|
+
}
|
|
52
|
+
// Posts
|
|
53
|
+
async createPost(data) {
|
|
54
|
+
return this.request("POST", "/api/v1/posts", data);
|
|
55
|
+
}
|
|
56
|
+
async listPosts(params) {
|
|
57
|
+
return this.request("GET", "/api/v1/posts", void 0, params);
|
|
58
|
+
}
|
|
59
|
+
async getPost(id) {
|
|
60
|
+
return this.request("GET", `/api/v1/posts/${id}`);
|
|
61
|
+
}
|
|
62
|
+
async updatePost(id, data) {
|
|
63
|
+
return this.request("PUT", `/api/v1/posts/${id}`, data);
|
|
64
|
+
}
|
|
65
|
+
async deletePost(id) {
|
|
66
|
+
return this.request("DELETE", `/api/v1/posts/${id}`);
|
|
67
|
+
}
|
|
68
|
+
async retryPost(id) {
|
|
69
|
+
return this.request("POST", `/api/v1/posts/${id}/retry`);
|
|
70
|
+
}
|
|
71
|
+
// Analytics
|
|
72
|
+
async getAnalytics(params) {
|
|
73
|
+
return this.request("GET", "/api/v1/analytics", void 0, params);
|
|
74
|
+
}
|
|
75
|
+
// Inbox
|
|
76
|
+
async listConversations(params) {
|
|
77
|
+
return this.request("GET", "/api/v1/inbox/conversations", void 0, params);
|
|
78
|
+
}
|
|
79
|
+
async getConversation(id) {
|
|
80
|
+
return this.request("GET", `/api/v1/inbox/conversations/${id}`);
|
|
81
|
+
}
|
|
82
|
+
async replyToConversation(id, message) {
|
|
83
|
+
return this.request("POST", `/api/v1/inbox/conversations/${id}/reply`, {
|
|
84
|
+
message
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
async listComments(params) {
|
|
88
|
+
return this.request("GET", "/api/v1/inbox/comments", void 0, params);
|
|
89
|
+
}
|
|
90
|
+
async replyToComment(id, message) {
|
|
91
|
+
return this.request("POST", `/api/v1/inbox/comments/${id}/reply`, {
|
|
92
|
+
message
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async listReviews(params) {
|
|
96
|
+
return this.request("GET", "/api/v1/inbox/reviews", void 0, params);
|
|
97
|
+
}
|
|
98
|
+
async replyToReview(id, message) {
|
|
99
|
+
return this.request("POST", `/api/v1/inbox/reviews/${id}/reply`, {
|
|
100
|
+
message
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
// Media
|
|
104
|
+
async uploadMedia(filename, base64Data, mimeType) {
|
|
105
|
+
return this.request("POST", "/api/v1/media/upload-base64", {
|
|
106
|
+
filename,
|
|
107
|
+
data: base64Data,
|
|
108
|
+
mimeType
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
async listMedia() {
|
|
112
|
+
return this.request("GET", "/api/v1/media");
|
|
113
|
+
}
|
|
114
|
+
async deleteMedia(key) {
|
|
115
|
+
return this.request("DELETE", `/api/v1/media/${encodeURIComponent(key)}`);
|
|
116
|
+
}
|
|
117
|
+
// Newsletter
|
|
118
|
+
async listSubscribers(params) {
|
|
119
|
+
return this.request(
|
|
120
|
+
"GET",
|
|
121
|
+
"/api/v1/newsletters/subscribers",
|
|
122
|
+
void 0,
|
|
123
|
+
params
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
async addSubscriber(data) {
|
|
127
|
+
return this.request("POST", "/api/v1/newsletters/subscribers", data);
|
|
128
|
+
}
|
|
129
|
+
async listBroadcasts(params) {
|
|
130
|
+
return this.request(
|
|
131
|
+
"GET",
|
|
132
|
+
"/api/v1/newsletters/broadcasts",
|
|
133
|
+
void 0,
|
|
134
|
+
params
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
async createBroadcast(data) {
|
|
138
|
+
return this.request("POST", "/api/v1/newsletters/broadcasts", data);
|
|
139
|
+
}
|
|
140
|
+
async updateBroadcast(id, data) {
|
|
141
|
+
return this.request("PUT", `/api/v1/newsletters/broadcasts/${id}`, data);
|
|
142
|
+
}
|
|
143
|
+
async sendBroadcast(id) {
|
|
144
|
+
return this.request("POST", `/api/v1/newsletters/broadcasts/${id}/send`);
|
|
145
|
+
}
|
|
146
|
+
async listTags() {
|
|
147
|
+
return this.request("GET", "/api/v1/newsletters/tags");
|
|
148
|
+
}
|
|
149
|
+
async listSequences() {
|
|
150
|
+
return this.request("GET", "/api/v1/newsletters/sequences");
|
|
151
|
+
}
|
|
152
|
+
async listForms() {
|
|
153
|
+
return this.request("GET", "/api/v1/newsletters/forms");
|
|
154
|
+
}
|
|
155
|
+
// RSS
|
|
156
|
+
async fetchFeed(url, limit) {
|
|
157
|
+
const params = { url };
|
|
158
|
+
if (limit !== void 0) params.limit = String(limit);
|
|
159
|
+
return this.request("GET", "/api/v1/rss/fetch", void 0, params);
|
|
160
|
+
}
|
|
161
|
+
async fetchArticle(url) {
|
|
162
|
+
return this.request("GET", "/api/v1/rss/article", void 0, { url });
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// src/tools/posts.ts
|
|
167
|
+
import { z } from "zod";
|
|
168
|
+
function registerPostTools(server2, client2) {
|
|
169
|
+
server2.tool(
|
|
170
|
+
"post",
|
|
171
|
+
"Create and publish a post to one or more social media platforms. Supports Twitter, Instagram, LinkedIn, and Facebook with full platform-specific options.",
|
|
172
|
+
{
|
|
173
|
+
content: z.string().optional().describe("The text content of the post"),
|
|
174
|
+
platforms: z.array(z.string()).describe('Platforms to post to, e.g. ["twitter", "linkedin"]'),
|
|
175
|
+
media_urls: z.array(z.string()).optional().describe("Public URLs of media to attach"),
|
|
176
|
+
platform_specific: z.record(z.record(z.unknown())).optional().describe(
|
|
177
|
+
"Platform-specific data keyed by platform name. E.g. { twitter: { threadItems: [...] }, instagram: { firstComment: '...' } }"
|
|
178
|
+
)
|
|
179
|
+
},
|
|
180
|
+
async (args) => {
|
|
181
|
+
const platforms = args.platforms.map((platform) => {
|
|
182
|
+
const entry = { platform };
|
|
183
|
+
if (args.platform_specific?.[platform]) {
|
|
184
|
+
entry.platformSpecificData = args.platform_specific[platform];
|
|
185
|
+
}
|
|
186
|
+
return entry;
|
|
187
|
+
});
|
|
188
|
+
const body = {
|
|
189
|
+
content: args.content,
|
|
190
|
+
platforms,
|
|
191
|
+
publishNow: true
|
|
192
|
+
};
|
|
193
|
+
if (args.media_urls?.length) {
|
|
194
|
+
body.mediaItems = args.media_urls.map((url) => ({
|
|
195
|
+
type: url.match(/\.(mp4|mov|avi|webm)$/i) ? "video" : "image",
|
|
196
|
+
url
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
const result = await client2.createPost(body);
|
|
200
|
+
return {
|
|
201
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
server2.tool(
|
|
206
|
+
"cross_post",
|
|
207
|
+
"Post the same content to all connected social media platforms at once.",
|
|
208
|
+
{
|
|
209
|
+
content: z.string().describe("The text content to post everywhere"),
|
|
210
|
+
media_urls: z.array(z.string()).optional().describe("Public URLs of media to attach")
|
|
211
|
+
},
|
|
212
|
+
async (args) => {
|
|
213
|
+
const accountsData = await client2.listAccounts();
|
|
214
|
+
const platforms = (accountsData.accounts ?? []).map(
|
|
215
|
+
(a) => a.platform
|
|
216
|
+
);
|
|
217
|
+
if (platforms.length === 0) {
|
|
218
|
+
return {
|
|
219
|
+
content: [
|
|
220
|
+
{
|
|
221
|
+
type: "text",
|
|
222
|
+
text: "No connected social accounts found. Connect accounts first."
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
const body = {
|
|
228
|
+
content: args.content,
|
|
229
|
+
platforms: platforms.map((p) => ({ platform: p })),
|
|
230
|
+
publishNow: true
|
|
231
|
+
};
|
|
232
|
+
if (args.media_urls?.length) {
|
|
233
|
+
body.mediaItems = args.media_urls.map((url) => ({
|
|
234
|
+
type: url.match(/\.(mp4|mov|avi|webm)$/i) ? "video" : "image",
|
|
235
|
+
url
|
|
236
|
+
}));
|
|
237
|
+
}
|
|
238
|
+
const result = await client2.createPost(body);
|
|
239
|
+
return {
|
|
240
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
server2.tool(
|
|
245
|
+
"schedule_post",
|
|
246
|
+
"Schedule a post for future publication.",
|
|
247
|
+
{
|
|
248
|
+
content: z.string().optional().describe("The text content of the post"),
|
|
249
|
+
platforms: z.array(z.string()).describe('Platforms to post to, e.g. ["twitter", "linkedin"]'),
|
|
250
|
+
scheduled_for: z.string().describe("ISO 8601 datetime for when to publish, e.g. 2024-01-16T12:00:00"),
|
|
251
|
+
timezone: z.string().default("UTC").describe("Timezone for the scheduled time, e.g. America/New_York"),
|
|
252
|
+
media_urls: z.array(z.string()).optional().describe("Media URLs to attach"),
|
|
253
|
+
platform_specific: z.record(z.record(z.unknown())).optional().describe("Platform-specific data keyed by platform name")
|
|
254
|
+
},
|
|
255
|
+
async (args) => {
|
|
256
|
+
const platforms = args.platforms.map((platform) => {
|
|
257
|
+
const entry = { platform };
|
|
258
|
+
if (args.platform_specific?.[platform]) {
|
|
259
|
+
entry.platformSpecificData = args.platform_specific[platform];
|
|
260
|
+
}
|
|
261
|
+
return entry;
|
|
262
|
+
});
|
|
263
|
+
const body = {
|
|
264
|
+
content: args.content,
|
|
265
|
+
platforms,
|
|
266
|
+
scheduledFor: args.scheduled_for,
|
|
267
|
+
timezone: args.timezone
|
|
268
|
+
};
|
|
269
|
+
if (args.media_urls?.length) {
|
|
270
|
+
body.mediaItems = args.media_urls.map((url) => ({
|
|
271
|
+
type: url.match(/\.(mp4|mov|avi|webm)$/i) ? "video" : "image",
|
|
272
|
+
url
|
|
273
|
+
}));
|
|
274
|
+
}
|
|
275
|
+
const result = await client2.createPost(body);
|
|
276
|
+
return {
|
|
277
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
server2.tool(
|
|
282
|
+
"create_draft",
|
|
283
|
+
"Save a post as a draft without publishing.",
|
|
284
|
+
{
|
|
285
|
+
content: z.string().optional().describe("The text content of the draft"),
|
|
286
|
+
platforms: z.array(z.string()).describe("Platforms this draft is intended for"),
|
|
287
|
+
media_urls: z.array(z.string()).optional().describe("Media URLs to attach")
|
|
288
|
+
},
|
|
289
|
+
async (args) => {
|
|
290
|
+
const body = {
|
|
291
|
+
content: args.content,
|
|
292
|
+
platforms: args.platforms.map((p) => ({ platform: p })),
|
|
293
|
+
publishNow: false
|
|
294
|
+
};
|
|
295
|
+
if (args.media_urls?.length) {
|
|
296
|
+
body.mediaItems = args.media_urls.map((url) => ({
|
|
297
|
+
type: url.match(/\.(mp4|mov|avi|webm)$/i) ? "video" : "image",
|
|
298
|
+
url
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
const result = await client2.createPost(body);
|
|
302
|
+
return {
|
|
303
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
);
|
|
307
|
+
server2.tool(
|
|
308
|
+
"list_posts",
|
|
309
|
+
"List recent posts with their status and platform details.",
|
|
310
|
+
{
|
|
311
|
+
status: z.string().optional().describe("Filter by status: published, scheduled, draft, failed"),
|
|
312
|
+
limit: z.string().optional().describe("Number of posts to return")
|
|
313
|
+
},
|
|
314
|
+
async (args) => {
|
|
315
|
+
const params = {};
|
|
316
|
+
if (args.status) params.status = args.status;
|
|
317
|
+
if (args.limit) params.limit = args.limit;
|
|
318
|
+
const result = await client2.listPosts(params);
|
|
319
|
+
return {
|
|
320
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
server2.tool(
|
|
325
|
+
"get_post",
|
|
326
|
+
"Get detailed information about a specific post.",
|
|
327
|
+
{
|
|
328
|
+
post_id: z.string().describe("The ID of the post to retrieve")
|
|
329
|
+
},
|
|
330
|
+
async (args) => {
|
|
331
|
+
const result = await client2.getPost(args.post_id);
|
|
332
|
+
return {
|
|
333
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
server2.tool(
|
|
338
|
+
"retry_post",
|
|
339
|
+
"Retry publishing a post that failed on one or more platforms.",
|
|
340
|
+
{
|
|
341
|
+
post_id: z.string().describe("The ID of the post to retry")
|
|
342
|
+
},
|
|
343
|
+
async (args) => {
|
|
344
|
+
const result = await client2.retryPost(args.post_id);
|
|
345
|
+
return {
|
|
346
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// src/tools/accounts.ts
|
|
353
|
+
function registerAccountTools(server2, client2) {
|
|
354
|
+
server2.tool(
|
|
355
|
+
"list_accounts",
|
|
356
|
+
"List all connected social media accounts with their platform, username, and connection status.",
|
|
357
|
+
{},
|
|
358
|
+
async () => {
|
|
359
|
+
const result = await client2.listAccounts();
|
|
360
|
+
return {
|
|
361
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
server2.tool(
|
|
366
|
+
"check_accounts_health",
|
|
367
|
+
"Check the health and connection status of all connected social media accounts.",
|
|
368
|
+
{},
|
|
369
|
+
async () => {
|
|
370
|
+
const result = await client2.checkAccountsHealth();
|
|
371
|
+
return {
|
|
372
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/tools/analytics.ts
|
|
379
|
+
import { z as z2 } from "zod";
|
|
380
|
+
function registerAnalyticsTools(server2, client2) {
|
|
381
|
+
server2.tool(
|
|
382
|
+
"get_analytics",
|
|
383
|
+
"Get performance analytics for your social media posts. Supports filtering by platform and date range.",
|
|
384
|
+
{
|
|
385
|
+
platform: z2.string().optional().describe("Filter by platform: twitter, instagram, linkedin, facebook"),
|
|
386
|
+
from_date: z2.string().optional().describe("Start date for analytics range (ISO 8601)"),
|
|
387
|
+
to_date: z2.string().optional().describe("End date for analytics range (ISO 8601)")
|
|
388
|
+
},
|
|
389
|
+
async (args) => {
|
|
390
|
+
const params = {};
|
|
391
|
+
if (args.platform) params.platform = args.platform;
|
|
392
|
+
if (args.from_date) params.fromDate = args.from_date;
|
|
393
|
+
if (args.to_date) params.toDate = args.to_date;
|
|
394
|
+
const result = await client2.getAnalytics(params);
|
|
395
|
+
return {
|
|
396
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// src/tools/inbox.ts
|
|
403
|
+
import { z as z3 } from "zod";
|
|
404
|
+
function registerInboxTools(server2, client2) {
|
|
405
|
+
server2.tool(
|
|
406
|
+
"list_conversations",
|
|
407
|
+
"List DM conversations across all connected social media platforms.",
|
|
408
|
+
{
|
|
409
|
+
platform: z3.string().optional().describe("Filter by platform: twitter, instagram, linkedin, facebook")
|
|
410
|
+
},
|
|
411
|
+
async (args) => {
|
|
412
|
+
const params = {};
|
|
413
|
+
if (args.platform) params.platform = args.platform;
|
|
414
|
+
const result = await client2.listConversations(params);
|
|
415
|
+
return {
|
|
416
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
server2.tool(
|
|
421
|
+
"get_conversation",
|
|
422
|
+
"Get all messages in a specific DM conversation.",
|
|
423
|
+
{
|
|
424
|
+
conversation_id: z3.string().describe("The conversation ID")
|
|
425
|
+
},
|
|
426
|
+
async (args) => {
|
|
427
|
+
const result = await client2.getConversation(args.conversation_id);
|
|
428
|
+
return {
|
|
429
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
);
|
|
433
|
+
server2.tool(
|
|
434
|
+
"reply_to_conversation",
|
|
435
|
+
"Send a reply message in a DM conversation.",
|
|
436
|
+
{
|
|
437
|
+
conversation_id: z3.string().describe("The conversation ID to reply to"),
|
|
438
|
+
message: z3.string().describe("The reply message text")
|
|
439
|
+
},
|
|
440
|
+
async (args) => {
|
|
441
|
+
const result = await client2.replyToConversation(
|
|
442
|
+
args.conversation_id,
|
|
443
|
+
args.message
|
|
444
|
+
);
|
|
445
|
+
return {
|
|
446
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
);
|
|
450
|
+
server2.tool(
|
|
451
|
+
"list_comments",
|
|
452
|
+
"List comments on your social media posts.",
|
|
453
|
+
{
|
|
454
|
+
post_id: z3.string().optional().describe("Filter comments by post ID")
|
|
455
|
+
},
|
|
456
|
+
async (args) => {
|
|
457
|
+
const params = {};
|
|
458
|
+
if (args.post_id) params.postId = args.post_id;
|
|
459
|
+
const result = await client2.listComments(params);
|
|
460
|
+
return {
|
|
461
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
);
|
|
465
|
+
server2.tool(
|
|
466
|
+
"reply_to_comment",
|
|
467
|
+
"Reply to a comment on one of your posts.",
|
|
468
|
+
{
|
|
469
|
+
comment_id: z3.string().describe("The comment ID to reply to"),
|
|
470
|
+
message: z3.string().describe("The reply text")
|
|
471
|
+
},
|
|
472
|
+
async (args) => {
|
|
473
|
+
const result = await client2.replyToComment(
|
|
474
|
+
args.comment_id,
|
|
475
|
+
args.message
|
|
476
|
+
);
|
|
477
|
+
return {
|
|
478
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
server2.tool(
|
|
483
|
+
"list_reviews",
|
|
484
|
+
"List reviews on your Facebook page.",
|
|
485
|
+
{},
|
|
486
|
+
async () => {
|
|
487
|
+
const result = await client2.listReviews();
|
|
488
|
+
return {
|
|
489
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
);
|
|
493
|
+
server2.tool(
|
|
494
|
+
"reply_to_review",
|
|
495
|
+
"Reply to a review on your Facebook page.",
|
|
496
|
+
{
|
|
497
|
+
review_id: z3.string().describe("The review ID to reply to"),
|
|
498
|
+
message: z3.string().describe("The reply text")
|
|
499
|
+
},
|
|
500
|
+
async (args) => {
|
|
501
|
+
const result = await client2.replyToReview(
|
|
502
|
+
args.review_id,
|
|
503
|
+
args.message
|
|
504
|
+
);
|
|
505
|
+
return {
|
|
506
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// src/tools/media.ts
|
|
513
|
+
import { z as z4 } from "zod";
|
|
514
|
+
import { readFile } from "fs/promises";
|
|
515
|
+
function registerMediaTools(server2, client2) {
|
|
516
|
+
server2.tool(
|
|
517
|
+
"upload_media",
|
|
518
|
+
"Upload an image or video file from a local file path. Returns a CDN URL that can be used in posts.",
|
|
519
|
+
{
|
|
520
|
+
file_path: z4.string().describe("Absolute path to the file on the local filesystem")
|
|
521
|
+
},
|
|
522
|
+
async (args) => {
|
|
523
|
+
const buffer = await readFile(args.file_path);
|
|
524
|
+
const base64 = buffer.toString("base64");
|
|
525
|
+
const filename = args.file_path.split("/").pop() ?? "upload";
|
|
526
|
+
const ext = filename.split(".").pop()?.toLowerCase() ?? "";
|
|
527
|
+
const mimeMap = {
|
|
528
|
+
jpg: "image/jpeg",
|
|
529
|
+
jpeg: "image/jpeg",
|
|
530
|
+
png: "image/png",
|
|
531
|
+
gif: "image/gif",
|
|
532
|
+
webp: "image/webp",
|
|
533
|
+
mp4: "video/mp4",
|
|
534
|
+
mov: "video/quicktime",
|
|
535
|
+
avi: "video/x-msvideo",
|
|
536
|
+
webm: "video/webm",
|
|
537
|
+
pdf: "application/pdf"
|
|
538
|
+
};
|
|
539
|
+
const mimeType = mimeMap[ext] ?? "application/octet-stream";
|
|
540
|
+
const result = await client2.uploadMedia(filename, base64, mimeType);
|
|
541
|
+
return {
|
|
542
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
);
|
|
546
|
+
server2.tool(
|
|
547
|
+
"upload_media_base64",
|
|
548
|
+
"Upload media from base64-encoded data. Returns a CDN URL that can be used in posts.",
|
|
549
|
+
{
|
|
550
|
+
filename: z4.string().describe("The filename including extension"),
|
|
551
|
+
data: z4.string().describe("Base64-encoded file data"),
|
|
552
|
+
mime_type: z4.string().describe("MIME type of the file, e.g. image/jpeg, video/mp4")
|
|
553
|
+
},
|
|
554
|
+
async (args) => {
|
|
555
|
+
const result = await client2.uploadMedia(
|
|
556
|
+
args.filename,
|
|
557
|
+
args.data,
|
|
558
|
+
args.mime_type
|
|
559
|
+
);
|
|
560
|
+
return {
|
|
561
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
);
|
|
565
|
+
server2.tool(
|
|
566
|
+
"list_media",
|
|
567
|
+
"List all uploaded media files in your BuzzPoster media library.",
|
|
568
|
+
{},
|
|
569
|
+
async () => {
|
|
570
|
+
const result = await client2.listMedia();
|
|
571
|
+
return {
|
|
572
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
);
|
|
576
|
+
server2.tool(
|
|
577
|
+
"delete_media",
|
|
578
|
+
"Delete a media file from your BuzzPoster media library.",
|
|
579
|
+
{
|
|
580
|
+
key: z4.string().describe("The key/path of the media file to delete")
|
|
581
|
+
},
|
|
582
|
+
async (args) => {
|
|
583
|
+
await client2.deleteMedia(args.key);
|
|
584
|
+
return {
|
|
585
|
+
content: [{ type: "text", text: "Media deleted successfully." }]
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// src/tools/newsletter.ts
|
|
592
|
+
import { z as z5 } from "zod";
|
|
593
|
+
function registerNewsletterTools(server2, client2) {
|
|
594
|
+
server2.tool(
|
|
595
|
+
"list_subscribers",
|
|
596
|
+
"List email subscribers from your configured email service provider (Kit, Beehiiv, or Mailchimp).",
|
|
597
|
+
{
|
|
598
|
+
page: z5.string().optional().describe("Page number for pagination"),
|
|
599
|
+
per_page: z5.string().optional().describe("Number of subscribers per page")
|
|
600
|
+
},
|
|
601
|
+
async (args) => {
|
|
602
|
+
const params = {};
|
|
603
|
+
if (args.page) params.page = args.page;
|
|
604
|
+
if (args.per_page) params.perPage = args.per_page;
|
|
605
|
+
const result = await client2.listSubscribers(params);
|
|
606
|
+
return {
|
|
607
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
);
|
|
611
|
+
server2.tool(
|
|
612
|
+
"add_subscriber",
|
|
613
|
+
"Add a new email subscriber to your mailing list.",
|
|
614
|
+
{
|
|
615
|
+
email: z5.string().describe("Email address of the subscriber"),
|
|
616
|
+
first_name: z5.string().optional().describe("Subscriber's first name"),
|
|
617
|
+
last_name: z5.string().optional().describe("Subscriber's last name")
|
|
618
|
+
},
|
|
619
|
+
async (args) => {
|
|
620
|
+
const result = await client2.addSubscriber({
|
|
621
|
+
email: args.email,
|
|
622
|
+
firstName: args.first_name,
|
|
623
|
+
lastName: args.last_name
|
|
624
|
+
});
|
|
625
|
+
return {
|
|
626
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
);
|
|
630
|
+
server2.tool(
|
|
631
|
+
"create_newsletter",
|
|
632
|
+
"Create a new newsletter/broadcast draft.",
|
|
633
|
+
{
|
|
634
|
+
subject: z5.string().describe("Email subject line"),
|
|
635
|
+
content: z5.string().describe("HTML content of the newsletter"),
|
|
636
|
+
preview_text: z5.string().optional().describe("Preview text shown in email clients")
|
|
637
|
+
},
|
|
638
|
+
async (args) => {
|
|
639
|
+
const result = await client2.createBroadcast({
|
|
640
|
+
subject: args.subject,
|
|
641
|
+
content: args.content,
|
|
642
|
+
previewText: args.preview_text
|
|
643
|
+
});
|
|
644
|
+
return {
|
|
645
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
);
|
|
649
|
+
server2.tool(
|
|
650
|
+
"update_newsletter",
|
|
651
|
+
"Update an existing newsletter/broadcast draft.",
|
|
652
|
+
{
|
|
653
|
+
broadcast_id: z5.string().describe("The broadcast/newsletter ID to update"),
|
|
654
|
+
subject: z5.string().optional().describe("Updated subject line"),
|
|
655
|
+
content: z5.string().optional().describe("Updated HTML content"),
|
|
656
|
+
preview_text: z5.string().optional().describe("Updated preview text")
|
|
657
|
+
},
|
|
658
|
+
async (args) => {
|
|
659
|
+
const data = {};
|
|
660
|
+
if (args.subject) data.subject = args.subject;
|
|
661
|
+
if (args.content) data.content = args.content;
|
|
662
|
+
if (args.preview_text) data.previewText = args.preview_text;
|
|
663
|
+
const result = await client2.updateBroadcast(args.broadcast_id, data);
|
|
664
|
+
return {
|
|
665
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
);
|
|
669
|
+
server2.tool(
|
|
670
|
+
"send_newsletter",
|
|
671
|
+
"Send a newsletter/broadcast to subscribers. This action cannot be undone.",
|
|
672
|
+
{
|
|
673
|
+
broadcast_id: z5.string().describe("The broadcast/newsletter ID to send")
|
|
674
|
+
},
|
|
675
|
+
async (args) => {
|
|
676
|
+
await client2.sendBroadcast(args.broadcast_id);
|
|
677
|
+
return {
|
|
678
|
+
content: [{ type: "text", text: "Newsletter sent successfully." }]
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
);
|
|
682
|
+
server2.tool(
|
|
683
|
+
"list_newsletters",
|
|
684
|
+
"List all newsletters/broadcasts with their status.",
|
|
685
|
+
{
|
|
686
|
+
page: z5.string().optional().describe("Page number for pagination")
|
|
687
|
+
},
|
|
688
|
+
async (args) => {
|
|
689
|
+
const params = {};
|
|
690
|
+
if (args.page) params.page = args.page;
|
|
691
|
+
const result = await client2.listBroadcasts(params);
|
|
692
|
+
return {
|
|
693
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
);
|
|
697
|
+
server2.tool(
|
|
698
|
+
"list_tags",
|
|
699
|
+
"List all subscriber tags from your email service provider.",
|
|
700
|
+
{},
|
|
701
|
+
async () => {
|
|
702
|
+
const result = await client2.listTags();
|
|
703
|
+
return {
|
|
704
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
);
|
|
708
|
+
server2.tool(
|
|
709
|
+
"list_sequences",
|
|
710
|
+
"List all email sequences/automations from your email service provider.",
|
|
711
|
+
{},
|
|
712
|
+
async () => {
|
|
713
|
+
const result = await client2.listSequences();
|
|
714
|
+
return {
|
|
715
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
);
|
|
719
|
+
server2.tool(
|
|
720
|
+
"list_forms",
|
|
721
|
+
"List all signup forms from your email service provider.",
|
|
722
|
+
{},
|
|
723
|
+
async () => {
|
|
724
|
+
const result = await client2.listForms();
|
|
725
|
+
return {
|
|
726
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// src/tools/rss.ts
|
|
733
|
+
import { z as z6 } from "zod";
|
|
734
|
+
function registerRssTools(server2, client2) {
|
|
735
|
+
server2.tool(
|
|
736
|
+
"fetch_feed",
|
|
737
|
+
"Fetch and parse entries from an RSS or Atom feed URL. Returns titles, links, descriptions, and dates.",
|
|
738
|
+
{
|
|
739
|
+
url: z6.string().describe("The RSS/Atom feed URL to fetch"),
|
|
740
|
+
limit: z6.number().optional().describe("Maximum number of entries to return (default 10, max 100)")
|
|
741
|
+
},
|
|
742
|
+
async (args) => {
|
|
743
|
+
const result = await client2.fetchFeed(args.url, args.limit);
|
|
744
|
+
return {
|
|
745
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
);
|
|
749
|
+
server2.tool(
|
|
750
|
+
"fetch_article",
|
|
751
|
+
"Extract the full article content from a URL as clean text/markdown. Useful for reading and summarizing articles.",
|
|
752
|
+
{
|
|
753
|
+
url: z6.string().describe("The article URL to extract content from")
|
|
754
|
+
},
|
|
755
|
+
async (args) => {
|
|
756
|
+
const result = await client2.fetchArticle(args.url);
|
|
757
|
+
return {
|
|
758
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// src/tools/account.ts
|
|
765
|
+
function registerAccountInfoTool(server2, client2) {
|
|
766
|
+
server2.tool(
|
|
767
|
+
"get_account",
|
|
768
|
+
"Get your BuzzPoster account details including name, email, subscription status, ESP configuration, and connected platforms.",
|
|
769
|
+
{},
|
|
770
|
+
async () => {
|
|
771
|
+
const result = await client2.getAccount();
|
|
772
|
+
return {
|
|
773
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// src/index.ts
|
|
780
|
+
var apiKey = process.env.BUZZPOSTER_API_KEY;
|
|
781
|
+
var apiUrl = process.env.BUZZPOSTER_API_URL ?? "https://api.buzzposter.com";
|
|
782
|
+
if (!apiKey) {
|
|
783
|
+
console.error(
|
|
784
|
+
"Error: BUZZPOSTER_API_KEY environment variable is required."
|
|
785
|
+
);
|
|
786
|
+
console.error(
|
|
787
|
+
"Set it in your Claude Desktop MCP config or export it in your shell."
|
|
788
|
+
);
|
|
789
|
+
process.exit(1);
|
|
790
|
+
}
|
|
791
|
+
var client = new BuzzPosterClient({ baseUrl: apiUrl, apiKey });
|
|
792
|
+
var server = new McpServer({
|
|
793
|
+
name: "buzzposter",
|
|
794
|
+
version: "0.1.0"
|
|
795
|
+
});
|
|
796
|
+
registerPostTools(server, client);
|
|
797
|
+
registerAccountTools(server, client);
|
|
798
|
+
registerAnalyticsTools(server, client);
|
|
799
|
+
registerInboxTools(server, client);
|
|
800
|
+
registerMediaTools(server, client);
|
|
801
|
+
registerNewsletterTools(server, client);
|
|
802
|
+
registerRssTools(server, client);
|
|
803
|
+
registerAccountInfoTool(server, client);
|
|
804
|
+
var transport = new StdioServerTransport();
|
|
805
|
+
await server.connect(transport);
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@buzzposter/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "BuzzPoster MCP server - Social media, newsletters, and media hosting for Claude Desktop",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"buzzposter-mcp": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"dev": "tsx watch src/index.ts",
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"typecheck": "tsc --noEmit"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"claude",
|
|
22
|
+
"social-media",
|
|
23
|
+
"newsletter",
|
|
24
|
+
"buzzposter",
|
|
25
|
+
"model-context-protocol"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
30
|
+
"zod": "^3.24.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"typescript": "^5.7.0",
|
|
34
|
+
"tsx": "^4.19.0",
|
|
35
|
+
"tsup": "^8.3.0",
|
|
36
|
+
"@types/node": "^22.0.0"
|
|
37
|
+
}
|
|
38
|
+
}
|