@buzzposter/mcp 0.3.6 → 0.3.7

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.js CHANGED
@@ -118,6 +118,79 @@ var BuzzPosterClient = class {
118
118
  async uploadFromUrl(data) {
119
119
  return this.request("POST", "/api/v1/media/upload-from-url", data);
120
120
  }
121
+ async uploadFile(filePath, filename) {
122
+ const fs4 = await import("fs");
123
+ const pathMod = await import("path");
124
+ const buffer = fs4.readFileSync(filePath);
125
+ const name = filename || pathMod.basename(filePath);
126
+ const ext = pathMod.extname(name).toLowerCase();
127
+ const mimeMap = {
128
+ ".jpg": "image/jpeg",
129
+ ".jpeg": "image/jpeg",
130
+ ".png": "image/png",
131
+ ".gif": "image/gif",
132
+ ".webp": "image/webp",
133
+ ".svg": "image/svg+xml",
134
+ ".mp4": "video/mp4",
135
+ ".mov": "video/quicktime",
136
+ ".webm": "video/webm"
137
+ };
138
+ const mime = mimeMap[ext] || "application/octet-stream";
139
+ const formData = new FormData();
140
+ formData.append("file", new Blob([buffer], { type: mime }), name);
141
+ const url = new URL(`${this.baseUrl}/api/v1/media/upload`);
142
+ const headers = {
143
+ Authorization: `Bearer ${this.apiKey}`
144
+ };
145
+ if (this.organizationId) {
146
+ headers["X-Organization-Id"] = this.organizationId;
147
+ }
148
+ const res = await fetch(url, { method: "POST", headers, body: formData });
149
+ if (!res.ok) {
150
+ const errorBody = await res.json().catch(() => ({ message: res.statusText }));
151
+ let message = `API error (${res.status})`;
152
+ if (errorBody && typeof errorBody === "object") {
153
+ const b = errorBody;
154
+ if (typeof b.message === "string" && b.message) message = b.message;
155
+ else if (typeof b.error === "string" && b.error) message = b.error;
156
+ }
157
+ throw new Error(`BuzzPoster API error (${res.status}): ${message}`);
158
+ }
159
+ const text = await res.text();
160
+ return text ? JSON.parse(text) : void 0;
161
+ }
162
+ async uploadBase64(data, filename, contentType) {
163
+ const buffer = Buffer.from(data, "base64");
164
+ const mime = contentType || "application/octet-stream";
165
+ const formData = new FormData();
166
+ formData.append("file", new Blob([buffer], { type: mime }), filename);
167
+ const url = new URL(`${this.baseUrl}/api/v1/media/upload`);
168
+ const headers = {
169
+ Authorization: `Bearer ${this.apiKey}`
170
+ };
171
+ if (this.organizationId) {
172
+ headers["X-Organization-Id"] = this.organizationId;
173
+ }
174
+ const res = await fetch(url, { method: "POST", headers, body: formData });
175
+ if (!res.ok) {
176
+ const errorBody = await res.json().catch(() => ({ message: res.statusText }));
177
+ let message = `API error (${res.status})`;
178
+ if (errorBody && typeof errorBody === "object") {
179
+ const b = errorBody;
180
+ if (typeof b.message === "string" && b.message) message = b.message;
181
+ else if (typeof b.error === "string" && b.error) message = b.error;
182
+ }
183
+ throw new Error(`BuzzPoster API error (${res.status}): ${message}`);
184
+ }
185
+ const text = await res.text();
186
+ return text ? JSON.parse(text) : void 0;
187
+ }
188
+ async presignUpload(filename, contentType) {
189
+ return this.request("POST", "/api/v1/media/presign", { filename, content_type: contentType });
190
+ }
191
+ async confirmUpload(key, filename, contentType) {
192
+ return this.request("POST", "/api/v1/media/confirm-upload", { key, filename, content_type: contentType });
193
+ }
121
194
  async listMedia() {
122
195
  return this.request("GET", "/api/v1/media");
123
196
  }
@@ -1258,14 +1331,17 @@ function registerMediaTools(server2, client2) {
1258
1331
  "manage_media",
1259
1332
  {
1260
1333
  title: "Manage Media",
1261
- description: "Browse assets gallery, upload from URL, list files, or delete media.",
1334
+ description: "Browse assets gallery, upload from URL, local file, or base64 data, list files, or delete media.",
1262
1335
  inputSchema: {
1263
- action: z3.enum(["get_assets", "upload", "list", "delete"]),
1336
+ action: z3.enum(["get_assets", "upload", "upload_file", "upload_base64", "presign", "confirm_upload", "list", "delete"]),
1264
1337
  limit: z3.number().optional().describe("Max assets to return (get_assets, default 10, max 20)"),
1265
1338
  url: z3.string().optional().describe("Public URL to upload (upload)"),
1266
- filename: z3.string().optional().describe("Filename override (upload)"),
1339
+ file_path: z3.string().optional().describe("Local file path to upload (upload_file)"),
1340
+ data: z3.string().optional().describe("Base64-encoded file contents (upload_base64)"),
1341
+ content_type: z3.string().optional().describe("MIME type e.g. image/png (upload_base64, presign, confirm_upload)"),
1342
+ filename: z3.string().optional().describe("Filename (upload, upload_file, upload_base64, presign, confirm_upload)"),
1267
1343
  folder: z3.string().optional().describe("Storage folder path (upload)"),
1268
- key: z3.string().optional().describe("Media file key/path (delete)"),
1344
+ key: z3.string().optional().describe("Media file key/path (delete, confirm_upload)"),
1269
1345
  confirmed: z3.boolean().default(false).describe("Confirm deletion (delete)")
1270
1346
  },
1271
1347
  annotations: {
@@ -1342,6 +1418,92 @@ function registerMediaTools(server2, client2) {
1342
1418
  content: [{ type: "text", text: lines.join("\n") }]
1343
1419
  };
1344
1420
  }
1421
+ if (args.action === "upload_file") {
1422
+ if (!args.file_path) {
1423
+ return { content: [{ type: "text", text: "file_path is required for upload_file." }], isError: true };
1424
+ }
1425
+ if (!fs2.existsSync(args.file_path)) {
1426
+ return { content: [{ type: "text", text: `File not found: ${args.file_path}` }], isError: true };
1427
+ }
1428
+ const result = await client2.uploadFile(args.file_path, args.filename);
1429
+ const lines = [];
1430
+ lines.push("## Upload Complete");
1431
+ lines.push("");
1432
+ lines.push(`**URL:** ${result.url ?? "unknown"}`);
1433
+ lines.push(`**Filename:** ${result.filename ?? "unknown"}`);
1434
+ lines.push(`**Size:** ${formatSize(result.size)}`);
1435
+ lines.push(`**Type:** ${result.mimeType ?? "unknown"}`);
1436
+ return {
1437
+ content: [{ type: "text", text: lines.join("\n") }]
1438
+ };
1439
+ }
1440
+ if (args.action === "upload_base64") {
1441
+ if (!args.data) {
1442
+ return { content: [{ type: "text", text: "data (base64 string) is required for upload_base64." }], isError: true };
1443
+ }
1444
+ if (!args.filename) {
1445
+ return { content: [{ type: "text", text: "filename is required for upload_base64." }], isError: true };
1446
+ }
1447
+ const result = await client2.uploadBase64(args.data, args.filename, args.content_type);
1448
+ const lines = [];
1449
+ lines.push("## Upload Complete");
1450
+ lines.push("");
1451
+ lines.push(`**URL:** ${result.url ?? "unknown"}`);
1452
+ lines.push(`**Filename:** ${result.filename ?? "unknown"}`);
1453
+ lines.push(`**Size:** ${formatSize(result.size)}`);
1454
+ lines.push(`**Type:** ${result.mimeType ?? "unknown"}`);
1455
+ return {
1456
+ content: [{ type: "text", text: lines.join("\n") }]
1457
+ };
1458
+ }
1459
+ if (args.action === "presign") {
1460
+ if (!args.filename) {
1461
+ return { content: [{ type: "text", text: "filename is required for presign." }], isError: true };
1462
+ }
1463
+ if (!args.content_type) {
1464
+ return { content: [{ type: "text", text: "content_type is required for presign (e.g. image/png)." }], isError: true };
1465
+ }
1466
+ const result = await client2.presignUpload(args.filename, args.content_type);
1467
+ const lines = [];
1468
+ lines.push("## Presigned Upload URL");
1469
+ lines.push("");
1470
+ lines.push(`**Upload URL:** ${result.upload_url}`);
1471
+ lines.push(`**CDN URL:** ${result.cdn_url}`);
1472
+ lines.push(`**Key:** ${result.key}`);
1473
+ lines.push(`**Expires:** ${result.expires_at}`);
1474
+ lines.push("");
1475
+ lines.push("**Step 1:** Upload the file:");
1476
+ lines.push("```");
1477
+ lines.push(`curl -X PUT -H "Content-Type: ${args.content_type}" --data-binary @${args.filename} "${result.upload_url}"`);
1478
+ lines.push("```");
1479
+ lines.push("");
1480
+ lines.push(`**Step 2:** Register the upload by calling this tool with action=confirm_upload, key="${result.key}", filename="${args.filename}", content_type="${args.content_type}"`);
1481
+ return {
1482
+ content: [{ type: "text", text: lines.join("\n") }]
1483
+ };
1484
+ }
1485
+ if (args.action === "confirm_upload") {
1486
+ if (!args.key) {
1487
+ return { content: [{ type: "text", text: "key is required for confirm_upload." }], isError: true };
1488
+ }
1489
+ if (!args.filename) {
1490
+ return { content: [{ type: "text", text: "filename is required for confirm_upload." }], isError: true };
1491
+ }
1492
+ if (!args.content_type) {
1493
+ return { content: [{ type: "text", text: "content_type is required for confirm_upload." }], isError: true };
1494
+ }
1495
+ const result = await client2.confirmUpload(args.key, args.filename, args.content_type);
1496
+ const lines = [];
1497
+ lines.push("## Upload Confirmed");
1498
+ lines.push("");
1499
+ lines.push(`**URL:** ${result.url}`);
1500
+ lines.push(`**Filename:** ${result.filename}`);
1501
+ lines.push(`**Size:** ${formatSize(result.size)}`);
1502
+ lines.push(`**Type:** ${result.mimeType}`);
1503
+ return {
1504
+ content: [{ type: "text", text: lines.join("\n") }]
1505
+ };
1506
+ }
1345
1507
  if (args.action === "list") {
1346
1508
  const result = await client2.listMedia();
1347
1509
  const items = result?.media ?? [];
package/dist/tools.d.ts CHANGED
@@ -32,6 +32,22 @@ declare class BuzzPosterClient {
32
32
  filename?: string;
33
33
  folder?: string;
34
34
  }): Promise<unknown>;
35
+ uploadFile(filePath: string, filename?: string): Promise<any>;
36
+ uploadBase64(data: string, filename: string, contentType?: string): Promise<any>;
37
+ presignUpload(filename: string, contentType: string): Promise<{
38
+ upload_url: string;
39
+ cdn_url: string;
40
+ key: string;
41
+ expires_at: string;
42
+ }>;
43
+ confirmUpload(key: string, filename: string, contentType: string): Promise<{
44
+ id: number;
45
+ key: string;
46
+ filename: string;
47
+ mimeType: string;
48
+ size: number;
49
+ url: string;
50
+ }>;
35
51
  listMedia(): Promise<unknown>;
36
52
  deleteMedia(key: string): Promise<unknown>;
37
53
  listSubscribers(params?: Record<string, string>): Promise<unknown>;
package/dist/tools.js CHANGED
@@ -112,6 +112,79 @@ var BuzzPosterClient = class {
112
112
  async uploadFromUrl(data) {
113
113
  return this.request("POST", "/api/v1/media/upload-from-url", data);
114
114
  }
115
+ async uploadFile(filePath, filename) {
116
+ const fs4 = await import("fs");
117
+ const pathMod = await import("path");
118
+ const buffer = fs4.readFileSync(filePath);
119
+ const name = filename || pathMod.basename(filePath);
120
+ const ext = pathMod.extname(name).toLowerCase();
121
+ const mimeMap = {
122
+ ".jpg": "image/jpeg",
123
+ ".jpeg": "image/jpeg",
124
+ ".png": "image/png",
125
+ ".gif": "image/gif",
126
+ ".webp": "image/webp",
127
+ ".svg": "image/svg+xml",
128
+ ".mp4": "video/mp4",
129
+ ".mov": "video/quicktime",
130
+ ".webm": "video/webm"
131
+ };
132
+ const mime = mimeMap[ext] || "application/octet-stream";
133
+ const formData = new FormData();
134
+ formData.append("file", new Blob([buffer], { type: mime }), name);
135
+ const url = new URL(`${this.baseUrl}/api/v1/media/upload`);
136
+ const headers = {
137
+ Authorization: `Bearer ${this.apiKey}`
138
+ };
139
+ if (this.organizationId) {
140
+ headers["X-Organization-Id"] = this.organizationId;
141
+ }
142
+ const res = await fetch(url, { method: "POST", headers, body: formData });
143
+ if (!res.ok) {
144
+ const errorBody = await res.json().catch(() => ({ message: res.statusText }));
145
+ let message = `API error (${res.status})`;
146
+ if (errorBody && typeof errorBody === "object") {
147
+ const b = errorBody;
148
+ if (typeof b.message === "string" && b.message) message = b.message;
149
+ else if (typeof b.error === "string" && b.error) message = b.error;
150
+ }
151
+ throw new Error(`BuzzPoster API error (${res.status}): ${message}`);
152
+ }
153
+ const text = await res.text();
154
+ return text ? JSON.parse(text) : void 0;
155
+ }
156
+ async uploadBase64(data, filename, contentType) {
157
+ const buffer = Buffer.from(data, "base64");
158
+ const mime = contentType || "application/octet-stream";
159
+ const formData = new FormData();
160
+ formData.append("file", new Blob([buffer], { type: mime }), filename);
161
+ const url = new URL(`${this.baseUrl}/api/v1/media/upload`);
162
+ const headers = {
163
+ Authorization: `Bearer ${this.apiKey}`
164
+ };
165
+ if (this.organizationId) {
166
+ headers["X-Organization-Id"] = this.organizationId;
167
+ }
168
+ const res = await fetch(url, { method: "POST", headers, body: formData });
169
+ if (!res.ok) {
170
+ const errorBody = await res.json().catch(() => ({ message: res.statusText }));
171
+ let message = `API error (${res.status})`;
172
+ if (errorBody && typeof errorBody === "object") {
173
+ const b = errorBody;
174
+ if (typeof b.message === "string" && b.message) message = b.message;
175
+ else if (typeof b.error === "string" && b.error) message = b.error;
176
+ }
177
+ throw new Error(`BuzzPoster API error (${res.status}): ${message}`);
178
+ }
179
+ const text = await res.text();
180
+ return text ? JSON.parse(text) : void 0;
181
+ }
182
+ async presignUpload(filename, contentType) {
183
+ return this.request("POST", "/api/v1/media/presign", { filename, content_type: contentType });
184
+ }
185
+ async confirmUpload(key, filename, contentType) {
186
+ return this.request("POST", "/api/v1/media/confirm-upload", { key, filename, content_type: contentType });
187
+ }
115
188
  async listMedia() {
116
189
  return this.request("GET", "/api/v1/media");
117
190
  }
@@ -1252,14 +1325,17 @@ function registerMediaTools(server, client) {
1252
1325
  "manage_media",
1253
1326
  {
1254
1327
  title: "Manage Media",
1255
- description: "Browse assets gallery, upload from URL, list files, or delete media.",
1328
+ description: "Browse assets gallery, upload from URL, local file, or base64 data, list files, or delete media.",
1256
1329
  inputSchema: {
1257
- action: z3.enum(["get_assets", "upload", "list", "delete"]),
1330
+ action: z3.enum(["get_assets", "upload", "upload_file", "upload_base64", "presign", "confirm_upload", "list", "delete"]),
1258
1331
  limit: z3.number().optional().describe("Max assets to return (get_assets, default 10, max 20)"),
1259
1332
  url: z3.string().optional().describe("Public URL to upload (upload)"),
1260
- filename: z3.string().optional().describe("Filename override (upload)"),
1333
+ file_path: z3.string().optional().describe("Local file path to upload (upload_file)"),
1334
+ data: z3.string().optional().describe("Base64-encoded file contents (upload_base64)"),
1335
+ content_type: z3.string().optional().describe("MIME type e.g. image/png (upload_base64, presign, confirm_upload)"),
1336
+ filename: z3.string().optional().describe("Filename (upload, upload_file, upload_base64, presign, confirm_upload)"),
1261
1337
  folder: z3.string().optional().describe("Storage folder path (upload)"),
1262
- key: z3.string().optional().describe("Media file key/path (delete)"),
1338
+ key: z3.string().optional().describe("Media file key/path (delete, confirm_upload)"),
1263
1339
  confirmed: z3.boolean().default(false).describe("Confirm deletion (delete)")
1264
1340
  },
1265
1341
  annotations: {
@@ -1336,6 +1412,92 @@ function registerMediaTools(server, client) {
1336
1412
  content: [{ type: "text", text: lines.join("\n") }]
1337
1413
  };
1338
1414
  }
1415
+ if (args.action === "upload_file") {
1416
+ if (!args.file_path) {
1417
+ return { content: [{ type: "text", text: "file_path is required for upload_file." }], isError: true };
1418
+ }
1419
+ if (!fs2.existsSync(args.file_path)) {
1420
+ return { content: [{ type: "text", text: `File not found: ${args.file_path}` }], isError: true };
1421
+ }
1422
+ const result = await client.uploadFile(args.file_path, args.filename);
1423
+ const lines = [];
1424
+ lines.push("## Upload Complete");
1425
+ lines.push("");
1426
+ lines.push(`**URL:** ${result.url ?? "unknown"}`);
1427
+ lines.push(`**Filename:** ${result.filename ?? "unknown"}`);
1428
+ lines.push(`**Size:** ${formatSize(result.size)}`);
1429
+ lines.push(`**Type:** ${result.mimeType ?? "unknown"}`);
1430
+ return {
1431
+ content: [{ type: "text", text: lines.join("\n") }]
1432
+ };
1433
+ }
1434
+ if (args.action === "upload_base64") {
1435
+ if (!args.data) {
1436
+ return { content: [{ type: "text", text: "data (base64 string) is required for upload_base64." }], isError: true };
1437
+ }
1438
+ if (!args.filename) {
1439
+ return { content: [{ type: "text", text: "filename is required for upload_base64." }], isError: true };
1440
+ }
1441
+ const result = await client.uploadBase64(args.data, args.filename, args.content_type);
1442
+ const lines = [];
1443
+ lines.push("## Upload Complete");
1444
+ lines.push("");
1445
+ lines.push(`**URL:** ${result.url ?? "unknown"}`);
1446
+ lines.push(`**Filename:** ${result.filename ?? "unknown"}`);
1447
+ lines.push(`**Size:** ${formatSize(result.size)}`);
1448
+ lines.push(`**Type:** ${result.mimeType ?? "unknown"}`);
1449
+ return {
1450
+ content: [{ type: "text", text: lines.join("\n") }]
1451
+ };
1452
+ }
1453
+ if (args.action === "presign") {
1454
+ if (!args.filename) {
1455
+ return { content: [{ type: "text", text: "filename is required for presign." }], isError: true };
1456
+ }
1457
+ if (!args.content_type) {
1458
+ return { content: [{ type: "text", text: "content_type is required for presign (e.g. image/png)." }], isError: true };
1459
+ }
1460
+ const result = await client.presignUpload(args.filename, args.content_type);
1461
+ const lines = [];
1462
+ lines.push("## Presigned Upload URL");
1463
+ lines.push("");
1464
+ lines.push(`**Upload URL:** ${result.upload_url}`);
1465
+ lines.push(`**CDN URL:** ${result.cdn_url}`);
1466
+ lines.push(`**Key:** ${result.key}`);
1467
+ lines.push(`**Expires:** ${result.expires_at}`);
1468
+ lines.push("");
1469
+ lines.push("**Step 1:** Upload the file:");
1470
+ lines.push("```");
1471
+ lines.push(`curl -X PUT -H "Content-Type: ${args.content_type}" --data-binary @${args.filename} "${result.upload_url}"`);
1472
+ lines.push("```");
1473
+ lines.push("");
1474
+ lines.push(`**Step 2:** Register the upload by calling this tool with action=confirm_upload, key="${result.key}", filename="${args.filename}", content_type="${args.content_type}"`);
1475
+ return {
1476
+ content: [{ type: "text", text: lines.join("\n") }]
1477
+ };
1478
+ }
1479
+ if (args.action === "confirm_upload") {
1480
+ if (!args.key) {
1481
+ return { content: [{ type: "text", text: "key is required for confirm_upload." }], isError: true };
1482
+ }
1483
+ if (!args.filename) {
1484
+ return { content: [{ type: "text", text: "filename is required for confirm_upload." }], isError: true };
1485
+ }
1486
+ if (!args.content_type) {
1487
+ return { content: [{ type: "text", text: "content_type is required for confirm_upload." }], isError: true };
1488
+ }
1489
+ const result = await client.confirmUpload(args.key, args.filename, args.content_type);
1490
+ const lines = [];
1491
+ lines.push("## Upload Confirmed");
1492
+ lines.push("");
1493
+ lines.push(`**URL:** ${result.url}`);
1494
+ lines.push(`**Filename:** ${result.filename}`);
1495
+ lines.push(`**Size:** ${formatSize(result.size)}`);
1496
+ lines.push(`**Type:** ${result.mimeType}`);
1497
+ return {
1498
+ content: [{ type: "text", text: lines.join("\n") }]
1499
+ };
1500
+ }
1339
1501
  if (args.action === "list") {
1340
1502
  const result = await client.listMedia();
1341
1503
  const items = result?.media ?? [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buzzposter/mcp",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "BuzzPoster MCP server - Social media, newsletters, and media hosting for any AI chatbot",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",