postproxy-mcp 1.0.1 → 1.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/README.md +96 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +8 -0
- package/dist/api/client.js.map +1 -1
- package/dist/server.d.ts +27 -6
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +25 -6
- package/dist/server.js.map +1 -1
- package/dist/tools/history.d.ts.map +1 -1
- package/dist/tools/history.js +5 -2
- package/dist/tools/history.js.map +1 -1
- package/dist/tools/post.d.ts +4 -0
- package/dist/tools/post.d.ts.map +1 -1
- package/dist/tools/post.js +20 -8
- package/dist/tools/post.js.map +1 -1
- package/dist/types/index.d.ts +36 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/validation.d.ts +63 -2
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +12 -0
- package/dist/utils/validation.js.map +1 -1
- package/package.json +1 -1
- package/src/api/client.ts +10 -0
- package/src/server.ts +25 -6
- package/src/tools/history.ts +4 -2
- package/src/tools/post.ts +25 -14
- package/src/types/index.ts +34 -11
- package/src/utils/validation.ts +13 -0
- package/worker/index.ts +76 -19
package/src/types/index.ts
CHANGED
|
@@ -21,6 +21,20 @@ export interface Profile {
|
|
|
21
21
|
avatar_url?: string;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export interface ThreadChild {
|
|
25
|
+
body: string;
|
|
26
|
+
media?: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface MediaAttachment {
|
|
30
|
+
id: string;
|
|
31
|
+
status: "pending" | "processed" | "failed";
|
|
32
|
+
error_message: string | null;
|
|
33
|
+
content_type: string;
|
|
34
|
+
source_url: string | null;
|
|
35
|
+
url: string | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
24
38
|
export interface CreatePostParams {
|
|
25
39
|
content: string;
|
|
26
40
|
profile_group_id?: number; // Not used by API, kept for compatibility
|
|
@@ -30,17 +44,20 @@ export interface CreatePostParams {
|
|
|
30
44
|
idempotency_key?: string;
|
|
31
45
|
draft?: boolean; // If true, creates a draft post that won't publish automatically
|
|
32
46
|
platforms?: PlatformParams; // Platform-specific parameters
|
|
47
|
+
thread?: ThreadChild[]; // Thread posts (supported on X and Threads)
|
|
33
48
|
}
|
|
34
49
|
|
|
35
50
|
export interface CreatePostResponse {
|
|
36
51
|
id: string;
|
|
37
52
|
body?: string; // API returns "body" field
|
|
38
53
|
content?: string; // Some responses use "content"
|
|
39
|
-
status: "draft" | "pending" | "processing" | "processed" | "scheduled";
|
|
54
|
+
status: "draft" | "pending" | "processing" | "processed" | "scheduled" | "media_processing_failed";
|
|
40
55
|
draft: boolean;
|
|
41
56
|
scheduled_at: string | null;
|
|
42
57
|
created_at: string;
|
|
58
|
+
media?: MediaAttachment[];
|
|
43
59
|
platforms: PlatformOutcome[];
|
|
60
|
+
thread?: Array<{ id: string; body: string; media?: MediaAttachment[] }>;
|
|
44
61
|
}
|
|
45
62
|
|
|
46
63
|
export interface PlatformOutcome {
|
|
@@ -62,24 +79,28 @@ export interface PostDetails {
|
|
|
62
79
|
id: string;
|
|
63
80
|
body?: string; // API returns "body" field
|
|
64
81
|
content?: string; // Some responses use "content"
|
|
65
|
-
status: "draft" | "pending" | "processing" | "processed" | "scheduled";
|
|
82
|
+
status: "draft" | "pending" | "processing" | "processed" | "scheduled" | "media_processing_failed";
|
|
66
83
|
draft: boolean;
|
|
67
84
|
scheduled_at: string | null;
|
|
68
85
|
created_at: string;
|
|
69
86
|
updated_at?: string;
|
|
87
|
+
media?: MediaAttachment[];
|
|
70
88
|
platforms: PlatformOutcome[];
|
|
89
|
+
thread?: Array<{ id: string; body: string; media?: MediaAttachment[] }>;
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
export interface Post {
|
|
74
93
|
id: string;
|
|
75
94
|
body?: string; // API returns "body" field
|
|
76
95
|
content?: string; // Some responses use "content"
|
|
77
|
-
status: "draft" | "pending" | "processing" | "processed" | "scheduled";
|
|
96
|
+
status: "draft" | "pending" | "processing" | "processed" | "scheduled" | "media_processing_failed";
|
|
78
97
|
draft: boolean;
|
|
79
98
|
scheduled_at: string | null;
|
|
80
99
|
created_at: string;
|
|
81
100
|
updated_at?: string;
|
|
101
|
+
media?: MediaAttachment[];
|
|
82
102
|
platforms: PlatformOutcome[];
|
|
103
|
+
thread?: Array<{ id: string; body: string; media?: MediaAttachment[] }>;
|
|
83
104
|
}
|
|
84
105
|
|
|
85
106
|
/**
|
|
@@ -102,21 +123,23 @@ export interface YouTubeParams {
|
|
|
102
123
|
title?: string; // Video title
|
|
103
124
|
privacy_status?: "public" | "unlisted" | "private"; // Video visibility
|
|
104
125
|
cover_url?: string; // Custom thumbnail URL
|
|
126
|
+
made_for_kids?: boolean; // Whether the video is made for kids
|
|
105
127
|
}
|
|
106
128
|
|
|
107
129
|
/**
|
|
108
130
|
* Platform-specific parameters for TikTok
|
|
109
131
|
*/
|
|
110
132
|
export interface TikTokParams {
|
|
133
|
+
format?: "video" | "image"; // Content format (video is default)
|
|
111
134
|
privacy_status?: "PUBLIC_TO_EVERYONE" | "MUTUAL_FOLLOW_FRIENDS" | "FOLLOWER_OF_CREATOR" | "SELF_ONLY";
|
|
112
|
-
photo_cover_index?: number; // Index (0-based) of photo to use as cover
|
|
113
|
-
auto_add_music?: boolean; // Enable automatic music
|
|
114
|
-
made_with_ai?: boolean; // Mark content as AI-generated
|
|
115
|
-
disable_comment?: boolean; // Disable comments
|
|
116
|
-
disable_duet?: boolean; // Disable duets
|
|
117
|
-
disable_stitch?: boolean; // Disable stitches
|
|
118
|
-
brand_content_toggle?: boolean; // Mark
|
|
119
|
-
brand_organic_toggle?: boolean; // Mark
|
|
135
|
+
photo_cover_index?: number; // Index (0-based) of photo to use as cover (image format)
|
|
136
|
+
auto_add_music?: boolean; // Enable automatic music (image format)
|
|
137
|
+
made_with_ai?: boolean; // Mark content as AI-generated (video format)
|
|
138
|
+
disable_comment?: boolean; // Disable comments
|
|
139
|
+
disable_duet?: boolean; // Disable duets (video format)
|
|
140
|
+
disable_stitch?: boolean; // Disable stitches (video format)
|
|
141
|
+
brand_content_toggle?: boolean; // Mark as paid partnership promoting a third-party business
|
|
142
|
+
brand_organic_toggle?: boolean; // Mark as paid partnership promoting your own brand
|
|
120
143
|
}
|
|
121
144
|
|
|
122
145
|
/**
|
package/src/utils/validation.ts
CHANGED
|
@@ -89,10 +89,14 @@ export const YouTubeParamsSchema = z.object({
|
|
|
89
89
|
errorMap: () => ({ message: "YouTube privacy_status must be 'public', 'unlisted', or 'private'" }),
|
|
90
90
|
}).optional(),
|
|
91
91
|
cover_url: URLSchema.optional(),
|
|
92
|
+
made_for_kids: z.boolean().optional(),
|
|
92
93
|
}).strict();
|
|
93
94
|
|
|
94
95
|
// TikTok parameters validation
|
|
95
96
|
export const TikTokParamsSchema = z.object({
|
|
97
|
+
format: z.enum(["video", "image"], {
|
|
98
|
+
errorMap: () => ({ message: "TikTok format must be 'video' or 'image'" }),
|
|
99
|
+
}).optional(),
|
|
96
100
|
privacy_status: z.enum([
|
|
97
101
|
"PUBLIC_TO_EVERYONE",
|
|
98
102
|
"MUTUAL_FOLLOW_FRIENDS",
|
|
@@ -142,6 +146,14 @@ export const PlatformParamsSchema = z.object({
|
|
|
142
146
|
threads: ThreadsParamsSchema.optional(),
|
|
143
147
|
}).strict().optional();
|
|
144
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Schema for thread child posts
|
|
151
|
+
*/
|
|
152
|
+
export const ThreadChildSchema = z.object({
|
|
153
|
+
body: z.string().min(1, "Thread child body cannot be empty"),
|
|
154
|
+
media: z.array(MediaItemSchema).optional(),
|
|
155
|
+
});
|
|
156
|
+
|
|
145
157
|
/**
|
|
146
158
|
* Schema for post.publish parameters
|
|
147
159
|
*/
|
|
@@ -154,6 +166,7 @@ export const PostPublishSchema = z.object({
|
|
|
154
166
|
require_confirmation: z.boolean().optional(),
|
|
155
167
|
draft: z.boolean().optional(),
|
|
156
168
|
platforms: PlatformParamsSchema,
|
|
169
|
+
thread: z.array(ThreadChildSchema).optional(),
|
|
157
170
|
});
|
|
158
171
|
|
|
159
172
|
/**
|
package/worker/index.ts
CHANGED
|
@@ -36,15 +36,26 @@ interface PlatformOutcome {
|
|
|
36
36
|
insights?: any;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
interface MediaAttachment {
|
|
40
|
+
id: string;
|
|
41
|
+
status: "pending" | "processed" | "failed";
|
|
42
|
+
error_message: string | null;
|
|
43
|
+
content_type: string;
|
|
44
|
+
source_url: string | null;
|
|
45
|
+
url: string | null;
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
interface Post {
|
|
40
49
|
id: string;
|
|
41
50
|
body?: string;
|
|
42
51
|
content?: string;
|
|
43
|
-
status: "draft" | "pending" | "processing" | "processed" | "scheduled";
|
|
52
|
+
status: "draft" | "pending" | "processing" | "processed" | "scheduled" | "media_processing_failed";
|
|
44
53
|
draft: boolean;
|
|
45
54
|
scheduled_at: string | null;
|
|
46
55
|
created_at: string;
|
|
56
|
+
media?: MediaAttachment[];
|
|
47
57
|
platforms: PlatformOutcome[];
|
|
58
|
+
thread?: Array<{ id: string; body: string; media?: MediaAttachment[] }>;
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
@@ -177,7 +188,8 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
177
188
|
schedule?: string,
|
|
178
189
|
draft?: boolean,
|
|
179
190
|
platformParams?: Record<string, Record<string, any>>,
|
|
180
|
-
idempotencyKey?: string
|
|
191
|
+
idempotencyKey?: string,
|
|
192
|
+
threadChildren?: Array<{ body: string; media?: string[] }>
|
|
181
193
|
): Promise<any> {
|
|
182
194
|
const baseUrl = this.env.POSTPROXY_BASE_URL.replace(/\/$/, "");
|
|
183
195
|
const url = `${baseUrl}/posts`;
|
|
@@ -224,6 +236,11 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
224
236
|
formData.append("platforms", JSON.stringify(platformParams));
|
|
225
237
|
}
|
|
226
238
|
|
|
239
|
+
// Add thread children
|
|
240
|
+
if (threadChildren && threadChildren.length > 0) {
|
|
241
|
+
formData.append("thread", JSON.stringify(threadChildren));
|
|
242
|
+
}
|
|
243
|
+
|
|
227
244
|
// Build headers
|
|
228
245
|
const headers: Record<string, string> = {
|
|
229
246
|
Authorization: `Bearer ${this.getApiKey()}`,
|
|
@@ -290,7 +307,10 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
290
307
|
*/
|
|
291
308
|
private determineOverallStatus(
|
|
292
309
|
post: Post
|
|
293
|
-
): "pending" | "processing" | "complete" | "failed" | "draft" {
|
|
310
|
+
): "pending" | "processing" | "complete" | "failed" | "draft" | "media_processing_failed" {
|
|
311
|
+
if (post.status === "media_processing_failed") {
|
|
312
|
+
return "media_processing_failed";
|
|
313
|
+
}
|
|
294
314
|
if (post.status === "draft" || post.draft === true) {
|
|
295
315
|
return "draft";
|
|
296
316
|
}
|
|
@@ -382,6 +402,7 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
382
402
|
* @param draft {boolean} If true, creates a draft post that won't publish automatically
|
|
383
403
|
* @param platforms {string} Optional JSON string of platform-specific parameters
|
|
384
404
|
* @param media_files {string} Optional JSON array of file objects with {filename, data (base64), content_type?}
|
|
405
|
+
* @param thread {string} Optional JSON array of thread child posts [{body, media?}]. Supported on X and Threads only.
|
|
385
406
|
* @return {Promise<string>} Post creation result as JSON
|
|
386
407
|
*/
|
|
387
408
|
async postPublish(
|
|
@@ -393,7 +414,8 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
393
414
|
require_confirmation?: boolean,
|
|
394
415
|
draft?: boolean,
|
|
395
416
|
platforms?: string,
|
|
396
|
-
media_files?: string
|
|
417
|
+
media_files?: string,
|
|
418
|
+
thread?: string
|
|
397
419
|
): Promise<string> {
|
|
398
420
|
this.getApiKey(); // Validate API key is present
|
|
399
421
|
|
|
@@ -432,6 +454,27 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
432
454
|
}
|
|
433
455
|
}
|
|
434
456
|
|
|
457
|
+
// Parse thread JSON if provided
|
|
458
|
+
let threadChildren: Array<{ body: string; media?: string[] }> | undefined;
|
|
459
|
+
if (thread) {
|
|
460
|
+
try {
|
|
461
|
+
threadChildren = JSON.parse(thread);
|
|
462
|
+
if (!Array.isArray(threadChildren)) {
|
|
463
|
+
throw new Error("thread must be an array");
|
|
464
|
+
}
|
|
465
|
+
for (const child of threadChildren) {
|
|
466
|
+
if (!child.body) {
|
|
467
|
+
throw new Error("Each thread child must have a 'body' property");
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
} catch (e: any) {
|
|
471
|
+
if (e.message.includes("thread")) {
|
|
472
|
+
throw e;
|
|
473
|
+
}
|
|
474
|
+
throw new Error("Invalid thread parameter: must be valid JSON array");
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
435
478
|
// Validate input
|
|
436
479
|
if (!content || content.trim() === "") {
|
|
437
480
|
throw new Error("Content cannot be empty");
|
|
@@ -452,6 +495,7 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
452
495
|
schedule_time: schedule,
|
|
453
496
|
draft: draft || false,
|
|
454
497
|
platforms: platformParams || {},
|
|
498
|
+
thread: threadChildren || [],
|
|
455
499
|
},
|
|
456
500
|
},
|
|
457
501
|
null,
|
|
@@ -503,7 +547,8 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
503
547
|
schedule,
|
|
504
548
|
draft,
|
|
505
549
|
platformParams,
|
|
506
|
-
finalIdempotencyKey
|
|
550
|
+
finalIdempotencyKey,
|
|
551
|
+
threadChildren
|
|
507
552
|
);
|
|
508
553
|
} else {
|
|
509
554
|
// Create post with JSON (URLs only)
|
|
@@ -527,6 +572,10 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
527
572
|
apiPayload.platforms = platformParams;
|
|
528
573
|
}
|
|
529
574
|
|
|
575
|
+
if (threadChildren && threadChildren.length > 0) {
|
|
576
|
+
apiPayload.thread = threadChildren;
|
|
577
|
+
}
|
|
578
|
+
|
|
530
579
|
const extraHeaders: Record<string, string> = {
|
|
531
580
|
"Idempotency-Key": finalIdempotencyKey,
|
|
532
581
|
};
|
|
@@ -579,17 +628,23 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
579
628
|
|
|
580
629
|
const overallStatus = this.determineOverallStatus(postDetails);
|
|
581
630
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
631
|
+
const result: any = {
|
|
632
|
+
job_id: job_id,
|
|
633
|
+
overall_status: overallStatus,
|
|
634
|
+
draft: postDetails.draft || false,
|
|
635
|
+
status: postDetails.status,
|
|
636
|
+
platforms,
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
if (postDetails.media && postDetails.media.length > 0) {
|
|
640
|
+
result.media = postDetails.media;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if (postDetails.thread && postDetails.thread.length > 0) {
|
|
644
|
+
result.thread = postDetails.thread;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
return JSON.stringify(result, null, 2);
|
|
593
648
|
}
|
|
594
649
|
|
|
595
650
|
/**
|
|
@@ -748,7 +803,7 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
748
803
|
},
|
|
749
804
|
{
|
|
750
805
|
name: "postPublish",
|
|
751
|
-
description: "Publish a post to specified targets",
|
|
806
|
+
description: "Publish a post to specified targets. Supports threads (X and Threads only).",
|
|
752
807
|
inputSchema: {
|
|
753
808
|
type: "object",
|
|
754
809
|
properties: {
|
|
@@ -759,8 +814,9 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
759
814
|
idempotency_key: { type: "string", description: "Optional idempotency key for deduplication" },
|
|
760
815
|
require_confirmation: { type: "boolean", description: "If true, return summary without publishing" },
|
|
761
816
|
draft: { type: "boolean", description: "If true, creates a draft post" },
|
|
762
|
-
platforms: { type: "string", description: "Optional JSON string of platform-specific parameters" },
|
|
817
|
+
platforms: { type: "string", description: "Optional JSON string of platform-specific parameters. YouTube supports: title, privacy_status, cover_url, made_for_kids. TikTok supports: format (video|image), privacy_status, and more." },
|
|
763
818
|
media_files: { type: "string", description: "Optional JSON array of file objects for direct upload. Each object must have 'filename' and 'data' (base64-encoded file content), optionally 'content_type'. Example: [{\"filename\":\"photo.jpg\",\"data\":\"base64...\"}]" },
|
|
819
|
+
thread: { type: "string", description: "Optional JSON array of thread child posts. Supported on X and Threads only. Each object must have 'body' (string), optionally 'media' (array of URLs). Example: [{\"body\":\"Reply 1\"},{\"body\":\"Reply 2\",\"media\":[\"https://...\"]}]" },
|
|
764
820
|
},
|
|
765
821
|
required: ["content", "targets"],
|
|
766
822
|
},
|
|
@@ -890,7 +946,8 @@ export default class PostProxyMCP extends WorkerEntrypoint<Env> {
|
|
|
890
946
|
args.require_confirmation,
|
|
891
947
|
args.draft,
|
|
892
948
|
args.platforms,
|
|
893
|
-
args.media_files
|
|
949
|
+
args.media_files,
|
|
950
|
+
args.thread
|
|
894
951
|
);
|
|
895
952
|
break;
|
|
896
953
|
case "postStatus":
|