posterly-mcp-server 0.8.2 → 0.19.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.
Files changed (121) hide show
  1. package/README.md +38 -3
  2. package/dist/generated/platform-manifest.d.ts +392 -0
  3. package/dist/generated/platform-manifest.js +695 -0
  4. package/dist/index.js +331 -1
  5. package/dist/lib/api-client.d.ts +391 -24
  6. package/dist/lib/api-client.js +176 -0
  7. package/dist/tools/audit-google-business-profile.d.ts +24 -0
  8. package/dist/tools/audit-google-business-profile.js +27 -0
  9. package/dist/tools/create-oauth-client.d.ts +40 -0
  10. package/dist/tools/create-oauth-client.js +20 -0
  11. package/dist/tools/create-post.d.ts +399 -20
  12. package/dist/tools/create-post.js +81 -78
  13. package/dist/tools/create-posts-batch.d.ts +567 -0
  14. package/dist/tools/create-posts-batch.js +38 -0
  15. package/dist/tools/create-signed-upload.d.ts +24 -0
  16. package/dist/tools/create-signed-upload.js +20 -0
  17. package/dist/tools/create-webhook.d.ts +36 -0
  18. package/dist/tools/create-webhook.js +20 -0
  19. package/dist/tools/delete-google-business-review-reply.d.ts +32 -0
  20. package/dist/tools/delete-google-business-review-reply.js +17 -0
  21. package/dist/tools/delete-oauth-client.d.ts +20 -0
  22. package/dist/tools/delete-oauth-client.js +13 -0
  23. package/dist/tools/delete-post-group.d.ts +20 -0
  24. package/dist/tools/delete-post-group.js +13 -0
  25. package/dist/tools/delete-webhook.d.ts +20 -0
  26. package/dist/tools/delete-webhook.js +13 -0
  27. package/dist/tools/disconnect-account.d.ts +20 -0
  28. package/dist/tools/disconnect-account.js +22 -0
  29. package/dist/tools/generate-image.d.ts +4 -4
  30. package/dist/tools/generate-video.d.ts +59 -0
  31. package/dist/tools/generate-video.js +34 -0
  32. package/dist/tools/get-connect-link.d.ts +24 -0
  33. package/dist/tools/get-connect-link.js +46 -0
  34. package/dist/tools/get-google-business-review-link.d.ts +24 -0
  35. package/dist/tools/get-google-business-review-link.js +19 -0
  36. package/dist/tools/get-platform-schema.d.ts +24 -0
  37. package/dist/tools/get-platform-schema.js +44 -0
  38. package/dist/tools/get-post-missing.d.ts +16 -0
  39. package/dist/tools/get-post-missing.js +12 -0
  40. package/dist/tools/get-video-job.d.ts +24 -0
  41. package/dist/tools/get-video-job.js +31 -0
  42. package/dist/tools/get-video-options.d.ts +8 -0
  43. package/dist/tools/get-video-options.js +26 -0
  44. package/dist/tools/get-x-posting-quota.d.ts +16 -0
  45. package/dist/tools/get-x-posting-quota.js +21 -0
  46. package/dist/tools/list-activity.d.ts +32 -0
  47. package/dist/tools/list-activity.js +35 -0
  48. package/dist/tools/list-google-business-reviews.d.ts +32 -0
  49. package/dist/tools/list-google-business-reviews.js +28 -0
  50. package/dist/tools/list-oauth-clients.d.ts +8 -0
  51. package/dist/tools/list-oauth-clients.js +15 -0
  52. package/dist/tools/list-platforms.d.ts +16 -0
  53. package/dist/tools/list-platforms.js +20 -0
  54. package/dist/tools/list-posts.d.ts +3 -3
  55. package/dist/tools/list-posts.js +3 -2
  56. package/dist/tools/list-webhooks.d.ts +16 -0
  57. package/dist/tools/list-webhooks.js +24 -0
  58. package/dist/tools/reply-google-business-review.d.ts +36 -0
  59. package/dist/tools/reply-google-business-review.js +18 -0
  60. package/dist/tools/run-video-function.d.ts +24 -0
  61. package/dist/tools/run-video-function.js +25 -0
  62. package/dist/tools/suggest-google-business-review-reply.d.ts +48 -0
  63. package/dist/tools/suggest-google-business-review-reply.js +27 -0
  64. package/dist/tools/test-webhook.d.ts +20 -0
  65. package/dist/tools/test-webhook.js +14 -0
  66. package/dist/tools/trigger-platform-helper.d.ts +28 -0
  67. package/dist/tools/trigger-platform-helper.js +22 -0
  68. package/dist/tools/update-oauth-client.d.ts +44 -0
  69. package/dist/tools/update-oauth-client.js +21 -0
  70. package/dist/tools/update-post-release-id.d.ts +28 -0
  71. package/dist/tools/update-post-release-id.js +18 -0
  72. package/dist/tools/update-post-status.d.ts +28 -0
  73. package/dist/tools/update-post-status.js +23 -0
  74. package/dist/tools/update-post.js +1 -1
  75. package/dist/tools/update-webhook.d.ts +40 -0
  76. package/dist/tools/update-webhook.js +21 -0
  77. package/dist/tools/upload-media-from-url.d.ts +24 -0
  78. package/dist/tools/upload-media-from-url.js +26 -0
  79. package/dist/tools/webhook-events.d.ts +3 -0
  80. package/dist/tools/webhook-events.js +13 -0
  81. package/package.json +1 -1
  82. package/src/generated/platform-manifest.ts +695 -0
  83. package/src/index.ts +496 -1
  84. package/src/lib/api-client.ts +497 -19
  85. package/src/tools/audit-google-business-profile.ts +38 -0
  86. package/src/tools/create-oauth-client.ts +36 -0
  87. package/src/tools/create-post.ts +91 -94
  88. package/src/tools/create-posts-batch.ts +48 -0
  89. package/src/tools/create-signed-upload.ts +27 -0
  90. package/src/tools/create-webhook.ts +35 -0
  91. package/src/tools/delete-google-business-review-reply.ts +30 -0
  92. package/src/tools/delete-oauth-client.ts +23 -0
  93. package/src/tools/delete-post-group.ts +17 -0
  94. package/src/tools/delete-webhook.ts +18 -0
  95. package/src/tools/disconnect-account.ts +30 -0
  96. package/src/tools/generate-video.ts +38 -0
  97. package/src/tools/get-connect-link.ts +56 -0
  98. package/src/tools/get-google-business-review-link.ts +30 -0
  99. package/src/tools/get-platform-schema.ts +52 -0
  100. package/src/tools/get-post-missing.ts +17 -0
  101. package/src/tools/get-video-job.ts +36 -0
  102. package/src/tools/get-video-options.ts +30 -0
  103. package/src/tools/get-x-posting-quota.ts +25 -0
  104. package/src/tools/list-activity.ts +51 -0
  105. package/src/tools/list-google-business-reviews.ts +43 -0
  106. package/src/tools/list-oauth-clients.ts +18 -0
  107. package/src/tools/list-platforms.ts +25 -0
  108. package/src/tools/list-posts.ts +3 -2
  109. package/src/tools/list-webhooks.ts +30 -0
  110. package/src/tools/reply-google-business-review.ts +32 -0
  111. package/src/tools/run-video-function.ts +32 -0
  112. package/src/tools/suggest-google-business-review-reply.ts +44 -0
  113. package/src/tools/test-webhook.ts +18 -0
  114. package/src/tools/trigger-platform-helper.ts +34 -0
  115. package/src/tools/update-oauth-client.ts +38 -0
  116. package/src/tools/update-post-release-id.ts +26 -0
  117. package/src/tools/update-post-status.ts +31 -0
  118. package/src/tools/update-post.ts +1 -1
  119. package/src/tools/update-webhook.ts +37 -0
  120. package/src/tools/upload-media-from-url.ts +33 -0
  121. package/src/tools/webhook-events.ts +16 -0
@@ -80,6 +80,37 @@ export interface InstagramPostSettings {
80
80
 
81
81
  export type PlatformPostSettings = Record<string, unknown>;
82
82
 
83
+ export type CreatePostPayload = {
84
+ account_id?: string;
85
+ username?: string;
86
+ platform?: string;
87
+ caption: string;
88
+ scheduled_at?: string;
89
+ post_type?: string;
90
+ media_url?: string;
91
+ media_urls?: string[];
92
+ metadata?: Record<string, unknown>;
93
+ settings?: PlatformPostSettings;
94
+ reel_cover_url?: string;
95
+ reel_cover_method?: string;
96
+ reel_thumb_offset?: number;
97
+ workspace_id?: string;
98
+ };
99
+
100
+ export type CreatePostResponse = {
101
+ post: Post;
102
+ workspace: { id: string; name: string; is_personal: boolean; resolved_from: 'explicit' | 'account' | 'personal' };
103
+ };
104
+
105
+ export type BatchCreatePostsResponse = {
106
+ posts: Array<CreatePostResponse & { index: number; replayed: boolean }>;
107
+ errors: Array<{ index: number; status: number; error: string; validation_errors?: unknown }>;
108
+ total: number;
109
+ created: number;
110
+ replayed: number;
111
+ failed: number;
112
+ };
113
+
83
114
  export interface Slot {
84
115
  time: string;
85
116
  local_time: string;
@@ -180,6 +211,172 @@ export interface PostAnalyticsResponse {
180
211
  offset: number;
181
212
  }
182
213
 
214
+ export interface GoogleBusinessReview {
215
+ name?: string;
216
+ reviewId?: string;
217
+ reviewer?: { displayName?: string; profilePhotoUrl?: string };
218
+ starRating: number;
219
+ comment?: string;
220
+ createTime?: string;
221
+ updateTime?: string;
222
+ reviewReply?: Record<string, unknown>;
223
+ locationId?: string | null;
224
+ locationName?: string | null;
225
+ businessName?: string | null;
226
+ socialAccountId?: number;
227
+ account_id?: number;
228
+ }
229
+
230
+ export interface GoogleBusinessReviewsResponse {
231
+ reviews: GoogleBusinessReview[];
232
+ total: number;
233
+ locations: Array<Record<string, unknown>>;
234
+ }
235
+
236
+ export interface GoogleBusinessReviewLinkResponse {
237
+ reviewLink: string;
238
+ placeId: string;
239
+ businessName: string;
240
+ locationId: string | null;
241
+ account_id: number;
242
+ }
243
+
244
+ export interface ActivityItem {
245
+ id: string;
246
+ type: 'activity' | 'publish';
247
+ post_id: number | null;
248
+ action?: string | null;
249
+ description?: string | null;
250
+ platform?: string | null;
251
+ outcome?: string | null;
252
+ error_code?: string | null;
253
+ error_message?: string | null;
254
+ http_status?: number | null;
255
+ retry_number?: number | null;
256
+ duration_ms?: number | null;
257
+ pipeline_step?: string | null;
258
+ metadata?: Record<string, unknown> | null;
259
+ context?: Record<string, unknown> | null;
260
+ created_at: string;
261
+ }
262
+
263
+ export interface ActivityResponse {
264
+ items: ActivityItem[];
265
+ total: number;
266
+ limit: number;
267
+ }
268
+
269
+ export interface ApiWebhook {
270
+ id: string;
271
+ workspace_id: string | null;
272
+ url: string;
273
+ description: string | null;
274
+ events: string[];
275
+ is_active: boolean;
276
+ created_at: string;
277
+ updated_at: string | null;
278
+ last_delivery_at: string | null;
279
+ last_delivery_status: number | null;
280
+ last_error: string | null;
281
+ }
282
+
283
+ export interface WebhookResponse {
284
+ webhook: ApiWebhook;
285
+ secret?: string;
286
+ }
287
+
288
+ export interface PlatformSchema {
289
+ id: string;
290
+ aliases: string[];
291
+ label: string;
292
+ status: 'supported' | 'planned';
293
+ summary: string;
294
+ content_limit: number | null;
295
+ media: Record<string, unknown>;
296
+ post_types: string[];
297
+ analytics_supported: boolean;
298
+ settings_schema: {
299
+ fields: Array<Record<string, unknown>>;
300
+ example: Record<string, unknown>;
301
+ };
302
+ helper_tools: Array<Record<string, unknown>>;
303
+ native_features: string[];
304
+ }
305
+
306
+ export interface ConnectOption {
307
+ platform: string;
308
+ aliases: string[];
309
+ label: string;
310
+ status: 'supported' | 'planned';
311
+ method: 'dashboard_oauth' | 'manual_credentials' | 'planned';
312
+ provider_platforms: string[];
313
+ dashboard_url: string;
314
+ connection_url: string | null;
315
+ oauth_url: string | null;
316
+ supports_direct_oauth_url: boolean;
317
+ requires_browser_session: boolean;
318
+ configured: boolean | null;
319
+ env_requirements: Array<Record<string, unknown>>;
320
+ missing_env: string[][];
321
+ scopes: string[];
322
+ credential_fields: Array<Record<string, unknown>>;
323
+ notes: string[];
324
+ connected_count?: number;
325
+ connected_accounts?: Account[];
326
+ }
327
+
328
+ export interface OAuthDeveloperClient {
329
+ id: string;
330
+ client_id: string;
331
+ client_name: string;
332
+ description?: string | null;
333
+ homepage?: string | null;
334
+ allowed_redirect_uris: string[];
335
+ default_scopes: string[];
336
+ is_active: boolean;
337
+ created_at?: string;
338
+ updated_at?: string;
339
+ }
340
+
341
+ export interface VideoJob {
342
+ id: string;
343
+ status: string;
344
+ prompt: string;
345
+ model: string;
346
+ duration_seconds: number;
347
+ aspect_ratio?: string;
348
+ resolution?: string;
349
+ generation_mode?: string | null;
350
+ credit_cost: number;
351
+ included_credit_cost?: number;
352
+ purchased_credit_cost?: number;
353
+ source_image_url?: string | null;
354
+ end_image_url?: string | null;
355
+ source_video_url?: string | null;
356
+ reference_images?: unknown;
357
+ generate_audio?: boolean | null;
358
+ video_url?: string | null;
359
+ error_message?: string | null;
360
+ created_at: string;
361
+ started_at?: string | null;
362
+ completed_at?: string | null;
363
+ }
364
+
365
+ export interface XPostingQuotaResponse {
366
+ workspace_id: string | null;
367
+ tier: string | null;
368
+ base_posts: number;
369
+ addon_posts: number;
370
+ total_posts: number;
371
+ used_posts: number;
372
+ remaining_posts: number;
373
+ current_period_start: string;
374
+ current_period_end: string;
375
+ urls_blocked: boolean;
376
+ addon?: Record<string, unknown>;
377
+ plans?: Array<Record<string, unknown>>;
378
+ }
379
+
183
380
  export class PosterlyClient {
184
381
  private baseUrl: string;
185
382
  private apiKey: string;
@@ -234,6 +431,58 @@ export class PosterlyClient {
234
431
  return data.accounts;
235
432
  }
236
433
 
434
+ async disconnectAccount(accountId: string): Promise<Record<string, any>> {
435
+ return this.request('DELETE', `/accounts/${encodeURIComponent(accountId)}`);
436
+ }
437
+
438
+ async getConnectLink(params?: {
439
+ platform?: string;
440
+ workspace_id?: string;
441
+ include_planned?: boolean;
442
+ }): Promise<{ connect?: ConnectOption; connect_options?: ConnectOption[] }> {
443
+ const searchParams = new URLSearchParams();
444
+ if (params?.workspace_id) searchParams.set('workspace_id', params.workspace_id);
445
+ if (params?.include_planned) searchParams.set('include_planned', 'true');
446
+ const qs = searchParams.toString();
447
+ const path = params?.platform
448
+ ? `/connect/${encodeURIComponent(params.platform)}`
449
+ : '/connect';
450
+ return this.request('GET', `${path}${qs ? `?${qs}` : ''}`);
451
+ }
452
+
453
+ async listOAuthClients(): Promise<{ clients: OAuthDeveloperClient[] }> {
454
+ return this.request('GET', '/oauth/clients');
455
+ }
456
+
457
+ async createOAuthClient(data: {
458
+ client_name: string;
459
+ description?: string;
460
+ homepage?: string;
461
+ allowed_redirect_uris: string[];
462
+ default_scopes: string[];
463
+ is_active?: boolean;
464
+ }): Promise<{ client: OAuthDeveloperClient }> {
465
+ return this.request('POST', '/oauth/clients', data);
466
+ }
467
+
468
+ async updateOAuthClient(
469
+ clientId: string,
470
+ data: {
471
+ client_name?: string;
472
+ description?: string;
473
+ homepage?: string | null;
474
+ allowed_redirect_uris?: string[];
475
+ default_scopes?: string[];
476
+ is_active?: boolean;
477
+ },
478
+ ): Promise<{ client: OAuthDeveloperClient }> {
479
+ return this.request('PATCH', `/oauth/clients/${encodeURIComponent(clientId)}`, data);
480
+ }
481
+
482
+ async deleteOAuthClient(clientId: string): Promise<{ deleted: boolean; client_id: string }> {
483
+ return this.request('DELETE', `/oauth/clients/${encodeURIComponent(clientId)}`);
484
+ }
485
+
237
486
  async listBrands(params?: { workspace_id?: string }): Promise<Brand[]> {
238
487
  const searchParams = new URLSearchParams();
239
488
  if (params?.workspace_id) searchParams.set('workspace_id', params.workspace_id);
@@ -275,32 +524,22 @@ export class PosterlyClient {
275
524
  return this.request('GET', `/posts${qs ? `?${qs}` : ''}`);
276
525
  }
277
526
 
278
- async createPost(data: {
279
- account_id?: string;
280
- username?: string;
281
- platform?: string;
282
- caption: string;
283
- scheduled_at?: string;
284
- post_type?: string;
285
- media_url?: string;
286
- media_urls?: string[];
287
- metadata?: Record<string, unknown>;
288
- settings?: PlatformPostSettings;
289
- reel_cover_url?: string;
290
- reel_cover_method?: string;
291
- reel_thumb_offset?: number;
292
- workspace_id?: string;
293
- }): Promise<{
294
- post: Post;
295
- workspace: { id: string; name: string; is_personal: boolean; resolved_from: 'explicit' | 'account' | 'personal' };
296
- }> {
527
+ async createPost(data: CreatePostPayload): Promise<CreatePostResponse> {
297
528
  return this.request('POST', '/posts', data);
298
529
  }
299
530
 
531
+ async createPostsBatch(data: { posts: CreatePostPayload[] }): Promise<BatchCreatePostsResponse> {
532
+ return this.request('POST', '/posts/batch', data);
533
+ }
534
+
300
535
  async getPost(id: number): Promise<{ post: Post }> {
301
536
  return this.request('GET', `/posts/${id}`);
302
537
  }
303
538
 
539
+ async getPostMissing(id: number): Promise<Record<string, any>> {
540
+ return this.request('GET', `/posts/${id}/missing`);
541
+ }
542
+
304
543
  async updatePost(
305
544
  id: number,
306
545
  data: {
@@ -323,6 +562,24 @@ export class PosterlyClient {
323
562
  return this.request('DELETE', `/posts/${id}`);
324
563
  }
325
564
 
565
+ async updatePostStatus(
566
+ id: number,
567
+ data: { status: 'scheduled' | 'paused' | 'draft'; scheduled_at?: string },
568
+ ): Promise<Record<string, any>> {
569
+ return this.request('PUT', `/posts/${id}/status`, data);
570
+ }
571
+
572
+ async updatePostReleaseId(
573
+ id: number,
574
+ data: { release_id: string; group_id?: string },
575
+ ): Promise<Record<string, any>> {
576
+ return this.request('PUT', `/posts/${id}/release-id`, data);
577
+ }
578
+
579
+ async deletePostGroup(groupId: string): Promise<Record<string, any>> {
580
+ return this.request('DELETE', `/posts/group/${encodeURIComponent(groupId)}?confirm=true`);
581
+ }
582
+
326
583
  async generateImage(data: {
327
584
  prompt: string;
328
585
  aspect_ratio?: string;
@@ -347,6 +604,49 @@ export class PosterlyClient {
347
604
  return this.request('POST', '/ai/generate-image', data);
348
605
  }
349
606
 
607
+ async getVideoOptions(): Promise<Record<string, any>> {
608
+ return this.request('GET', '/ai/video-options');
609
+ }
610
+
611
+ async runVideoFunction(data: {
612
+ identifier: string;
613
+ functionName: string;
614
+ params?: Record<string, unknown>;
615
+ }): Promise<Record<string, any>> {
616
+ return this.request('POST', '/ai/video-function', data);
617
+ }
618
+
619
+ async generateVideo(data: {
620
+ prompt: string;
621
+ negative_prompt?: string;
622
+ aspect_ratio?: '16:9' | '9:16';
623
+ duration_seconds?: 4 | 6 | 8;
624
+ resolution?: '720p' | '1080p';
625
+ model?: 'fast' | 'standard';
626
+ image_url?: string;
627
+ end_image_url?: string;
628
+ reference_images?: Array<{ url: string; type: 'character' | 'object' | 'scene' }>;
629
+ generate_audio?: boolean;
630
+ source_video_url?: string;
631
+ }): Promise<Record<string, any>> {
632
+ return this.request('POST', '/ai/generate-video', data);
633
+ }
634
+
635
+ async getVideoJob(params?: {
636
+ job_id?: string;
637
+ status?: string;
638
+ limit?: number;
639
+ }): Promise<{ job?: VideoJob; jobs?: VideoJob[] }> {
640
+ if (params?.job_id) {
641
+ return this.request('GET', `/ai/video-jobs/${encodeURIComponent(params.job_id)}`);
642
+ }
643
+ const searchParams = new URLSearchParams();
644
+ if (params?.status) searchParams.set('status', params.status);
645
+ if (params?.limit) searchParams.set('limit', String(params.limit));
646
+ const qs = searchParams.toString();
647
+ return this.request('GET', `/ai/video-jobs${qs ? `?${qs}` : ''}`);
648
+ }
649
+
350
650
  async findAvailableSlots(params?: {
351
651
  account_ids?: string[];
352
652
  timezone?: string;
@@ -392,6 +692,172 @@ export class PosterlyClient {
392
692
  return this.request('GET', `/analytics/posts?${searchParams.toString()}`);
393
693
  }
394
694
 
695
+ async listGoogleBusinessReviews(params?: {
696
+ workspace_id?: string;
697
+ account_id?: number;
698
+ location_id?: string;
699
+ rating?: number;
700
+ unanswered?: boolean;
701
+ }): Promise<GoogleBusinessReviewsResponse> {
702
+ const searchParams = new URLSearchParams();
703
+ if (params?.workspace_id) searchParams.set('workspace_id', params.workspace_id);
704
+ if (params?.account_id) searchParams.set('account_id', String(params.account_id));
705
+ if (params?.location_id) searchParams.set('location_id', params.location_id);
706
+ if (params?.rating) searchParams.set('rating', String(params.rating));
707
+ if (params?.unanswered) searchParams.set('unanswered', 'true');
708
+ const qs = searchParams.toString();
709
+ return this.request('GET', `/google-business/reviews${qs ? `?${qs}` : ''}`);
710
+ }
711
+
712
+ async getGoogleBusinessReviewLink(params: {
713
+ workspace_id?: string;
714
+ account_id?: number;
715
+ location_id?: string;
716
+ }): Promise<GoogleBusinessReviewLinkResponse> {
717
+ const searchParams = new URLSearchParams();
718
+ if (params.workspace_id) searchParams.set('workspace_id', params.workspace_id);
719
+ if (params.account_id) searchParams.set('account_id', String(params.account_id));
720
+ if (params.location_id) searchParams.set('location_id', params.location_id);
721
+ return this.request('GET', `/google-business/review-link?${searchParams.toString()}`);
722
+ }
723
+
724
+ async auditGoogleBusinessProfile(params: {
725
+ workspace_id?: string;
726
+ account_id?: number;
727
+ location_id?: string;
728
+ }): Promise<Record<string, any>> {
729
+ const searchParams = new URLSearchParams();
730
+ if (params.workspace_id) searchParams.set('workspace_id', params.workspace_id);
731
+ if (params.account_id) searchParams.set('account_id', String(params.account_id));
732
+ if (params.location_id) searchParams.set('location_id', params.location_id);
733
+ return this.request('GET', `/google-business/audit?${searchParams.toString()}`);
734
+ }
735
+
736
+ async suggestGoogleBusinessReviewReply(data: {
737
+ workspace_id?: string;
738
+ account_id?: number;
739
+ location_id?: string;
740
+ review_text?: string;
741
+ star_rating: number;
742
+ reviewer_name?: string;
743
+ business_name?: string;
744
+ tone?: 'professional' | 'friendly' | 'apologetic';
745
+ count?: number;
746
+ }): Promise<Record<string, any>> {
747
+ return this.request('POST', '/google-business/reviews/suggest-reply', data);
748
+ }
749
+
750
+ async replyGoogleBusinessReview(data: {
751
+ workspace_id?: string;
752
+ account_id?: number;
753
+ location_id?: string;
754
+ review_name: string;
755
+ comment: string;
756
+ }): Promise<Record<string, any>> {
757
+ return this.request('POST', '/google-business/reviews/reply', data);
758
+ }
759
+
760
+ async deleteGoogleBusinessReviewReply(data: {
761
+ workspace_id?: string;
762
+ account_id?: number;
763
+ location_id?: string;
764
+ review_name: string;
765
+ }): Promise<Record<string, any>> {
766
+ return this.request('DELETE', '/google-business/reviews/reply', data);
767
+ }
768
+
769
+ async listActivity(params?: {
770
+ workspace_id?: string;
771
+ post_id?: number;
772
+ since?: string;
773
+ type?: 'all' | 'activity' | 'publish';
774
+ limit?: number;
775
+ }): Promise<ActivityResponse> {
776
+ const searchParams = new URLSearchParams();
777
+ if (params?.workspace_id) searchParams.set('workspace_id', params.workspace_id);
778
+ if (params?.post_id) searchParams.set('post_id', String(params.post_id));
779
+ if (params?.since) searchParams.set('since', params.since);
780
+ if (params?.type) searchParams.set('type', params.type);
781
+ if (params?.limit) searchParams.set('limit', String(params.limit));
782
+ const qs = searchParams.toString();
783
+ return this.request('GET', `/activity${qs ? `?${qs}` : ''}`);
784
+ }
785
+
786
+ async listWebhooks(params?: { workspace_id?: string }): Promise<{ webhooks: ApiWebhook[] }> {
787
+ const searchParams = new URLSearchParams();
788
+ if (params?.workspace_id) searchParams.set('workspace_id', params.workspace_id);
789
+ const qs = searchParams.toString();
790
+ return this.request('GET', `/webhooks${qs ? `?${qs}` : ''}`);
791
+ }
792
+
793
+ async createWebhook(data: {
794
+ url: string;
795
+ events?: string[];
796
+ workspace_id?: string;
797
+ description?: string;
798
+ is_active?: boolean;
799
+ }): Promise<WebhookResponse> {
800
+ return this.request('POST', '/webhooks', data);
801
+ }
802
+
803
+ async updateWebhook(
804
+ id: string,
805
+ data: {
806
+ url?: string;
807
+ events?: string[];
808
+ workspace_id?: string | null;
809
+ description?: string | null;
810
+ is_active?: boolean;
811
+ },
812
+ ): Promise<WebhookResponse> {
813
+ return this.request('PATCH', `/webhooks/${encodeURIComponent(id)}`, data);
814
+ }
815
+
816
+ async deleteWebhook(id: string): Promise<{ deleted: boolean; id: string }> {
817
+ return this.request('DELETE', `/webhooks/${encodeURIComponent(id)}`);
818
+ }
819
+
820
+ async testWebhook(id: string): Promise<{ delivery: Record<string, unknown> }> {
821
+ return this.request('POST', `/webhooks/${encodeURIComponent(id)}/test`, {});
822
+ }
823
+
824
+ async listPlatforms(params?: { include_planned?: boolean }): Promise<{ platforms: PlatformSchema[] }> {
825
+ const searchParams = new URLSearchParams();
826
+ if (params?.include_planned) searchParams.set('include_planned', 'true');
827
+ const qs = searchParams.toString();
828
+ return this.request('GET', `/platforms${qs ? `?${qs}` : ''}`);
829
+ }
830
+
831
+ async getPlatformSchema(params: {
832
+ account_id?: string;
833
+ platform?: string;
834
+ include_planned?: boolean;
835
+ }): Promise<{ account?: Record<string, unknown>; schema?: PlatformSchema; platform?: PlatformSchema; platforms?: PlatformSchema[] }> {
836
+ if (params.account_id) {
837
+ return this.request('GET', `/accounts/${encodeURIComponent(params.account_id)}/schema`);
838
+ }
839
+ const searchParams = new URLSearchParams();
840
+ if (params.platform) searchParams.set('platform', params.platform);
841
+ if (params.include_planned) searchParams.set('include_planned', 'true');
842
+ return this.request('GET', `/platforms?${searchParams.toString()}`);
843
+ }
844
+
845
+ async triggerPlatformHelper(data: {
846
+ helper: string;
847
+ account_id?: string;
848
+ platform?: string;
849
+ params?: Record<string, unknown>;
850
+ }): Promise<{ helper: string; account: Record<string, unknown> | null; result: unknown }> {
851
+ return this.request('POST', '/platforms/trigger', data);
852
+ }
853
+
854
+ async getXPostingQuota(params?: { workspace_id?: string }): Promise<XPostingQuotaResponse> {
855
+ const searchParams = new URLSearchParams();
856
+ if (params?.workspace_id) searchParams.set('workspace_id', params.workspace_id);
857
+ const qs = searchParams.toString();
858
+ return this.request('GET', `/x-posting/quota${qs ? `?${qs}` : ''}`);
859
+ }
860
+
395
861
  async getSignedUploadUrl(
396
862
  filename: string,
397
863
  contentType: string,
@@ -459,6 +925,18 @@ export class PosterlyClient {
459
925
  content_type: contentType,
460
926
  });
461
927
  }
928
+
929
+ async uploadMediaFromUrl(input: {
930
+ url: string;
931
+ filename?: string;
932
+ contentType?: string;
933
+ }): Promise<{ url: string; path: string; source_url: string; content_type: string; size: number }> {
934
+ return this.request('POST', '/media/upload-from-url', {
935
+ url: input.url,
936
+ filename: input.filename,
937
+ content_type: input.contentType,
938
+ });
939
+ }
462
940
  }
463
941
 
464
942
  function guessContentType(filename: string): string {
@@ -0,0 +1,38 @@
1
+ import { z } from 'zod';
2
+ import type { PosterlyClient } from '../lib/api-client.js';
3
+
4
+ export const auditGoogleBusinessProfileTool = {
5
+ name: 'audit_google_business_profile',
6
+ description:
7
+ 'Run a live local-profile audit for a connected Google Business Profile location, including completeness, categories, media, attributes, reviews, and recommendations.',
8
+ inputSchema: z.object({
9
+ workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
10
+ account_id: z.number().int().positive().optional().describe('Google Business account ID from list_accounts.'),
11
+ location_id: z.string().optional().describe('Google Business location/platform_user_id.'),
12
+ }),
13
+
14
+ async execute(
15
+ client: PosterlyClient,
16
+ input: {
17
+ workspace_id?: string;
18
+ account_id?: number;
19
+ location_id?: string;
20
+ },
21
+ ) {
22
+ const result = await client.auditGoogleBusinessProfile(input);
23
+ const audit = result.audit || {};
24
+ const recommendations = Array.isArray(audit.recommendations) ? audit.recommendations : [];
25
+ const lines = [
26
+ `Google Business audit: ${audit.businessName || 'Unknown business'}`,
27
+ `Score: ${audit.overallScore ?? 'n/a'} (${audit.overallGrade || 'n/a'})`,
28
+ `Fetched: ${result.fetchedAt || 'unknown'}`,
29
+ ];
30
+ if (recommendations.length > 0) {
31
+ lines.push('Top recommendations:');
32
+ for (const rec of recommendations.slice(0, 5)) {
33
+ lines.push(`- ${rec.priority || 'medium'}: ${rec.description || rec.title || 'Recommendation'}`);
34
+ }
35
+ }
36
+ return lines.join('\n');
37
+ },
38
+ };
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod';
2
+ import type { PosterlyClient } from '../lib/api-client.js';
3
+
4
+ const scopeSchema = z.enum(['accounts:read', 'accounts:write', 'posts:read', 'posts:write', 'media:write', 'analytics:read']);
5
+
6
+ export const createOAuthClientTool = {
7
+ name: 'create_oauth_client',
8
+ description:
9
+ 'Create a public OAuth 2.1 + PKCE developer client. WRITE: confirm app name, redirect URIs, and scopes with the user before calling.',
10
+ inputSchema: z.object({
11
+ client_name: z.string().min(1).max(120),
12
+ description: z.string().max(500).optional(),
13
+ homepage: z.string().url().optional(),
14
+ allowed_redirect_uris: z.array(z.string().url()).min(1).max(10),
15
+ default_scopes: z.array(scopeSchema).min(1),
16
+ is_active: z.boolean().optional(),
17
+ confirm: z.literal(true).describe('Must be true after explicit user confirmation.'),
18
+ }),
19
+
20
+ async execute(
21
+ client: PosterlyClient,
22
+ input: {
23
+ client_name: string;
24
+ description?: string;
25
+ homepage?: string;
26
+ allowed_redirect_uris: string[];
27
+ default_scopes: string[];
28
+ is_active?: boolean;
29
+ confirm: true;
30
+ },
31
+ ) {
32
+ const { confirm: _confirm, ...payload } = input;
33
+ const result = await client.createOAuthClient(payload);
34
+ return `OAuth client created: ${result.client.client_name}\nclient_id: ${result.client.client_id}\nRedirect URIs: ${result.client.allowed_redirect_uris.join(', ')}`;
35
+ },
36
+ };