posterly-mcp-server 0.1.1 → 0.2.1
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/lib/api-client.d.ts
CHANGED
|
@@ -54,6 +54,12 @@ export declare class PosterlyClient {
|
|
|
54
54
|
timezone?: string;
|
|
55
55
|
count?: number;
|
|
56
56
|
}): Promise<Slot[]>;
|
|
57
|
+
getSignedUploadUrl(filename: string, contentType: string, size: number): Promise<{
|
|
58
|
+
upload_url: string;
|
|
59
|
+
token: string;
|
|
60
|
+
path: string;
|
|
61
|
+
public_url: string;
|
|
62
|
+
}>;
|
|
57
63
|
uploadMedia(input: {
|
|
58
64
|
filePath?: string;
|
|
59
65
|
base64Data?: string;
|
package/dist/lib/api-client.js
CHANGED
|
@@ -62,20 +62,45 @@ export class PosterlyClient {
|
|
|
62
62
|
const data = await this.request('GET', `/slots/next${qs ? `?${qs}` : ''}`);
|
|
63
63
|
return data.slots;
|
|
64
64
|
}
|
|
65
|
+
async getSignedUploadUrl(filename, contentType, size) {
|
|
66
|
+
return this.request('POST', '/media/signed-upload', {
|
|
67
|
+
filename,
|
|
68
|
+
content_type: contentType,
|
|
69
|
+
size,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
65
72
|
async uploadMedia(input) {
|
|
66
|
-
let
|
|
73
|
+
let fileBuffer;
|
|
67
74
|
if (input.filePath) {
|
|
68
|
-
|
|
69
|
-
data = buffer.toString('base64');
|
|
75
|
+
fileBuffer = await readFile(resolve(input.filePath));
|
|
70
76
|
}
|
|
71
77
|
else if (input.base64Data) {
|
|
72
|
-
|
|
78
|
+
fileBuffer = Buffer.from(input.base64Data, 'base64');
|
|
73
79
|
}
|
|
74
80
|
else {
|
|
75
81
|
throw new Error('Provide filePath or base64Data');
|
|
76
82
|
}
|
|
77
83
|
const contentType = input.contentType ||
|
|
78
84
|
guessContentType(input.filename);
|
|
85
|
+
const SIZE_THRESHOLD = 3 * 1024 * 1024; // 3MB
|
|
86
|
+
if (fileBuffer.length > SIZE_THRESHOLD) {
|
|
87
|
+
// Large file: use signed URL to upload directly to Supabase Storage
|
|
88
|
+
const signed = await this.getSignedUploadUrl(input.filename, contentType, fileBuffer.length);
|
|
89
|
+
const uploadRes = await fetch(signed.upload_url, {
|
|
90
|
+
method: 'PUT',
|
|
91
|
+
headers: {
|
|
92
|
+
'Content-Type': contentType,
|
|
93
|
+
},
|
|
94
|
+
body: new Uint8Array(fileBuffer),
|
|
95
|
+
});
|
|
96
|
+
if (!uploadRes.ok) {
|
|
97
|
+
const errText = await uploadRes.text().catch(() => uploadRes.statusText);
|
|
98
|
+
throw new Error(`Direct upload failed: ${errText}`);
|
|
99
|
+
}
|
|
100
|
+
return { url: signed.public_url, path: signed.path };
|
|
101
|
+
}
|
|
102
|
+
// Small file: use existing base64 flow through Vercel
|
|
103
|
+
const data = fileBuffer.toString('base64');
|
|
79
104
|
return this.request('POST', '/media/upload', {
|
|
80
105
|
filename: input.filename,
|
|
81
106
|
data,
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export const uploadMediaTool = {
|
|
3
3
|
name: 'upload_media',
|
|
4
|
-
description: 'Upload an image or video file to posterly. Returns a URL that can be used with create_post. Supports JPEG, PNG, GIF, WebP, MP4, MOV, WebM.
|
|
4
|
+
description: 'Upload an image or video file to posterly. Returns a URL that can be used with create_post. Supports JPEG, PNG, GIF, WebP, MP4, MOV, WebM. Images up to 10MB, videos up to 50MB. IMPORTANT: If the user shares an image in the chat, use base64_data (not file_path) since chat-uploaded images are not on the local filesystem. Only use file_path when the user provides an actual local filesystem path.',
|
|
5
5
|
inputSchema: z.object({
|
|
6
6
|
file_path: z
|
|
7
7
|
.string()
|
|
8
8
|
.optional()
|
|
9
|
-
.describe('Absolute path
|
|
9
|
+
.describe('Absolute local filesystem path (e.g. /Users/name/photo.jpg). Only use when the user gives you an explicit path. Do NOT guess paths for images uploaded in chat.'),
|
|
10
10
|
base64_data: z
|
|
11
11
|
.string()
|
|
12
12
|
.optional()
|
|
13
|
-
.describe('Base64-encoded file data
|
|
13
|
+
.describe('Base64-encoded file data. PREFERRED for images shared directly in chat — extract the image data as base64 and pass it here.'),
|
|
14
14
|
filename: z
|
|
15
15
|
.string()
|
|
16
16
|
.describe('Filename with extension (e.g. photo.jpg, video.mp4)'),
|
package/package.json
CHANGED
package/src/lib/api-client.ts
CHANGED
|
@@ -119,19 +119,30 @@ export class PosterlyClient {
|
|
|
119
119
|
return data.slots;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
async getSignedUploadUrl(
|
|
123
|
+
filename: string,
|
|
124
|
+
contentType: string,
|
|
125
|
+
size: number,
|
|
126
|
+
): Promise<{ upload_url: string; token: string; path: string; public_url: string }> {
|
|
127
|
+
return this.request('POST', '/media/signed-upload', {
|
|
128
|
+
filename,
|
|
129
|
+
content_type: contentType,
|
|
130
|
+
size,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
122
134
|
async uploadMedia(input: {
|
|
123
135
|
filePath?: string;
|
|
124
136
|
base64Data?: string;
|
|
125
137
|
filename: string;
|
|
126
138
|
contentType?: string;
|
|
127
139
|
}): Promise<{ url: string; path: string }> {
|
|
128
|
-
let
|
|
140
|
+
let fileBuffer: Buffer;
|
|
129
141
|
|
|
130
142
|
if (input.filePath) {
|
|
131
|
-
|
|
132
|
-
data = buffer.toString('base64');
|
|
143
|
+
fileBuffer = await readFile(resolve(input.filePath));
|
|
133
144
|
} else if (input.base64Data) {
|
|
134
|
-
|
|
145
|
+
fileBuffer = Buffer.from(input.base64Data, 'base64');
|
|
135
146
|
} else {
|
|
136
147
|
throw new Error('Provide filePath or base64Data');
|
|
137
148
|
}
|
|
@@ -140,6 +151,35 @@ export class PosterlyClient {
|
|
|
140
151
|
input.contentType ||
|
|
141
152
|
guessContentType(input.filename);
|
|
142
153
|
|
|
154
|
+
const SIZE_THRESHOLD = 3 * 1024 * 1024; // 3MB
|
|
155
|
+
|
|
156
|
+
if (fileBuffer.length > SIZE_THRESHOLD) {
|
|
157
|
+
// Large file: use signed URL to upload directly to Supabase Storage
|
|
158
|
+
const signed = await this.getSignedUploadUrl(
|
|
159
|
+
input.filename,
|
|
160
|
+
contentType,
|
|
161
|
+
fileBuffer.length,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const uploadRes = await fetch(signed.upload_url, {
|
|
165
|
+
method: 'PUT',
|
|
166
|
+
headers: {
|
|
167
|
+
'Content-Type': contentType,
|
|
168
|
+
},
|
|
169
|
+
body: new Uint8Array(fileBuffer),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
if (!uploadRes.ok) {
|
|
173
|
+
const errText = await uploadRes.text().catch(() => uploadRes.statusText);
|
|
174
|
+
throw new Error(`Direct upload failed: ${errText}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { url: signed.public_url, path: signed.path };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Small file: use existing base64 flow through Vercel
|
|
181
|
+
const data = fileBuffer.toString('base64');
|
|
182
|
+
|
|
143
183
|
return this.request('POST', '/media/upload', {
|
|
144
184
|
filename: input.filename,
|
|
145
185
|
data,
|
|
@@ -4,16 +4,16 @@ import type { PosterlyClient } from '../lib/api-client.js';
|
|
|
4
4
|
export const uploadMediaTool = {
|
|
5
5
|
name: 'upload_media',
|
|
6
6
|
description:
|
|
7
|
-
'Upload an image or video file to posterly. Returns a URL that can be used with create_post. Supports JPEG, PNG, GIF, WebP, MP4, MOV, WebM.
|
|
7
|
+
'Upload an image or video file to posterly. Returns a URL that can be used with create_post. Supports JPEG, PNG, GIF, WebP, MP4, MOV, WebM. Images up to 10MB, videos up to 50MB. IMPORTANT: If the user shares an image in the chat, use base64_data (not file_path) since chat-uploaded images are not on the local filesystem. Only use file_path when the user provides an actual local filesystem path.',
|
|
8
8
|
inputSchema: z.object({
|
|
9
9
|
file_path: z
|
|
10
10
|
.string()
|
|
11
11
|
.optional()
|
|
12
|
-
.describe('Absolute path
|
|
12
|
+
.describe('Absolute local filesystem path (e.g. /Users/name/photo.jpg). Only use when the user gives you an explicit path. Do NOT guess paths for images uploaded in chat.'),
|
|
13
13
|
base64_data: z
|
|
14
14
|
.string()
|
|
15
15
|
.optional()
|
|
16
|
-
.describe('Base64-encoded file data
|
|
16
|
+
.describe('Base64-encoded file data. PREFERRED for images shared directly in chat — extract the image data as base64 and pass it here.'),
|
|
17
17
|
filename: z
|
|
18
18
|
.string()
|
|
19
19
|
.describe('Filename with extension (e.g. photo.jpg, video.mp4)'),
|