posterly-mcp-server 0.23.1 → 0.24.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 (45) hide show
  1. package/README.md +12 -2
  2. package/dist/generated/platform-manifest.d.ts +82 -66
  3. package/dist/generated/platform-manifest.js +126 -77
  4. package/dist/index.js +40 -0
  5. package/dist/lib/api-client.d.ts +90 -0
  6. package/dist/lib/api-client.js +100 -0
  7. package/dist/lib/version.d.ts +1 -1
  8. package/dist/lib/version.js +1 -1
  9. package/dist/tools/create-connect-session.d.ts +3 -3
  10. package/dist/tools/create-connect-session.js +1 -1
  11. package/dist/tools/create-post.d.ts +27 -9
  12. package/dist/tools/create-post.js +1 -0
  13. package/dist/tools/create-posts-batch.d.ts +12 -5
  14. package/dist/tools/dismiss-suggestion.d.ts +16 -0
  15. package/dist/tools/dismiss-suggestion.js +18 -0
  16. package/dist/tools/get-account-analytics.js +1 -1
  17. package/dist/tools/get-connect-link.d.ts +3 -3
  18. package/dist/tools/get-performance-profile.d.ts +16 -0
  19. package/dist/tools/get-performance-profile.js +39 -0
  20. package/dist/tools/get-platform-schema.d.ts +3 -3
  21. package/dist/tools/get-post-analytics.js +1 -1
  22. package/dist/tools/get-post-insights.d.ts +28 -0
  23. package/dist/tools/get-post-insights.js +39 -0
  24. package/dist/tools/get-video-job.d.ts +2 -2
  25. package/dist/tools/list-activity.d.ts +2 -2
  26. package/dist/tools/list-post-suggestions.d.ts +24 -0
  27. package/dist/tools/list-post-suggestions.js +36 -0
  28. package/dist/tools/list-posts.d.ts +3 -3
  29. package/dist/tools/trigger-platform-helper.d.ts +6 -6
  30. package/dist/tools/update-post.d.ts +8 -0
  31. package/dist/tools/update-post.js +1 -0
  32. package/package.json +1 -1
  33. package/src/generated/platform-manifest.ts +126 -77
  34. package/src/index.ts +60 -0
  35. package/src/lib/api-client.ts +205 -0
  36. package/src/lib/version.ts +1 -1
  37. package/src/tools/create-connect-session.ts +1 -1
  38. package/src/tools/create-post.ts +1 -0
  39. package/src/tools/dismiss-suggestion.ts +22 -0
  40. package/src/tools/get-account-analytics.ts +1 -1
  41. package/src/tools/get-performance-profile.ts +47 -0
  42. package/src/tools/get-post-analytics.ts +1 -1
  43. package/src/tools/get-post-insights.ts +60 -0
  44. package/src/tools/list-post-suggestions.ts +56 -0
  45. package/src/tools/update-post.ts +1 -0
@@ -12,16 +12,17 @@ export const PLATFORM_MANIFEST = {
12
12
  "threads",
13
13
  "google_business",
14
14
  "telegram",
15
- "bluesky"
15
+ "bluesky",
16
+ "discord",
17
+ "mastodon",
18
+ "devto",
19
+ "hashnode",
20
+ "wordpress",
21
+ "lemmy"
16
22
  ],
17
23
  "plannedPlatformIds": [
18
24
  "reddit",
19
- "wordpress",
20
- "mastodon",
21
25
  "medium",
22
- "devto",
23
- "hashnode",
24
- "discord",
25
26
  "slack",
26
27
  "skool",
27
28
  "whop"
@@ -38,32 +39,39 @@ export const PLATFORM_MANIFEST = {
38
39
  "google_business",
39
40
  "telegram",
40
41
  "bluesky",
41
- "reddit",
42
- "wordpress",
42
+ "discord",
43
43
  "mastodon",
44
- "medium",
45
44
  "devto",
46
45
  "hashnode",
47
- "discord",
46
+ "wordpress",
47
+ "lemmy",
48
+ "reddit",
49
+ "medium",
48
50
  "slack",
49
51
  "skool",
50
52
  "whop"
51
53
  ],
52
54
  "supportedPlatformInputs": [
53
55
  "bluesky",
56
+ "devto",
57
+ "discord",
54
58
  "facebook",
55
59
  "gmb",
56
60
  "google-business",
57
61
  "google_business",
58
62
  "google_business_profile",
63
+ "hashnode",
59
64
  "instagram",
60
65
  "instagram-standalone",
66
+ "lemmy",
61
67
  "linkedin",
68
+ "mastodon",
62
69
  "pinterest",
63
70
  "telegram",
64
71
  "threads",
65
72
  "tiktok",
66
73
  "twitter",
74
+ "wordpress",
67
75
  "x",
68
76
  "youtube"
69
77
  ],
@@ -79,6 +87,7 @@ export const PLATFORM_MANIFEST = {
79
87
  "hashnode",
80
88
  "instagram",
81
89
  "instagram-standalone",
90
+ "lemmy",
82
91
  "linkedin",
83
92
  "mastodon",
84
93
  "medium",
@@ -106,6 +115,7 @@ export const PLATFORM_MANIFEST = {
106
115
  "hashnode": "hashnode",
107
116
  "instagram": "instagram",
108
117
  "instagram_standalone": "instagram",
118
+ "lemmy": "lemmy",
109
119
  "linkedin": "linkedin",
110
120
  "mastodon": "mastodon",
111
121
  "medium": "medium",
@@ -126,8 +136,7 @@ export const PLATFORM_MANIFEST = {
126
136
  "linkedin.recent_mentions",
127
137
  "pinterest.boards",
128
138
  "tiktok.creator_info",
129
- "x.quota",
130
- "youtube.playlists"
139
+ "x.quota"
131
140
  ],
132
141
  "analyticsPlatformIds": [
133
142
  "instagram",
@@ -135,6 +144,7 @@ export const PLATFORM_MANIFEST = {
135
144
  "linkedin",
136
145
  "youtube",
137
146
  "pinterest",
147
+ "threads",
138
148
  "google_business"
139
149
  ],
140
150
  "connectTargetIds": [
@@ -151,13 +161,14 @@ export const PLATFORM_MANIFEST = {
151
161
  "twitter",
152
162
  "telegram",
153
163
  "bluesky",
154
- "reddit",
155
- "wordpress",
164
+ "discord",
156
165
  "mastodon",
157
- "medium",
158
166
  "devto",
159
167
  "hashnode",
160
- "discord",
168
+ "wordpress",
169
+ "lemmy",
170
+ "reddit",
171
+ "medium",
161
172
  "slack",
162
173
  "skool",
163
174
  "whop"
@@ -177,6 +188,7 @@ export const PLATFORM_MANIFEST = {
177
188
  "instagram",
178
189
  "instagram-standalone",
179
190
  "instagram_direct",
191
+ "lemmy",
180
192
  "linkedin",
181
193
  "linkedin-company",
182
194
  "linkedin-page",
@@ -215,6 +227,7 @@ export const PLATFORM_MANIFEST = {
215
227
  "instagram": "instagram",
216
228
  "instagram_direct": "instagram",
217
229
  "instagram_standalone": "instagram",
230
+ "lemmy": "lemmy",
218
231
  "linkedin": "linkedin",
219
232
  "linkedin_company": "linkedin_page",
220
233
  "linkedin_page": "linkedin_page",
@@ -332,9 +345,7 @@ export const PLATFORM_MANIFEST = {
332
345
  "video"
333
346
  ],
334
347
  "analyticsSupported": true,
335
- "helpers": [
336
- "youtube.playlists"
337
- ]
348
+ "helpers": []
338
349
  },
339
350
  {
340
351
  "id": "pinterest",
@@ -363,7 +374,7 @@ export const PLATFORM_MANIFEST = {
363
374
  "carousel",
364
375
  "threads_thread"
365
376
  ],
366
- "analyticsSupported": false,
377
+ "analyticsSupported": true,
367
378
  "helpers": []
368
379
  },
369
380
  {
@@ -411,53 +422,84 @@ export const PLATFORM_MANIFEST = {
411
422
  "helpers": []
412
423
  },
413
424
  {
414
- "id": "reddit",
415
- "label": "Reddit",
416
- "status": "planned",
425
+ "id": "discord",
426
+ "label": "Discord",
427
+ "status": "supported",
417
428
  "aliases": [],
418
- "postTypes": [],
429
+ "postTypes": [
430
+ "text",
431
+ "image",
432
+ "video",
433
+ "carousel"
434
+ ],
419
435
  "analyticsSupported": false,
420
436
  "helpers": []
421
437
  },
422
438
  {
423
- "id": "wordpress",
424
- "label": "WordPress",
425
- "status": "planned",
439
+ "id": "mastodon",
440
+ "label": "Mastodon",
441
+ "status": "supported",
426
442
  "aliases": [],
427
- "postTypes": [],
443
+ "postTypes": [
444
+ "text",
445
+ "image",
446
+ "video",
447
+ "carousel"
448
+ ],
428
449
  "analyticsSupported": false,
429
450
  "helpers": []
430
451
  },
431
452
  {
432
- "id": "mastodon",
433
- "label": "Mastodon",
434
- "status": "planned",
453
+ "id": "devto",
454
+ "label": "Dev.to",
455
+ "status": "supported",
435
456
  "aliases": [],
436
- "postTypes": [],
457
+ "postTypes": [
458
+ "text",
459
+ "image"
460
+ ],
437
461
  "analyticsSupported": false,
438
462
  "helpers": []
439
463
  },
440
464
  {
441
- "id": "medium",
442
- "label": "Medium",
443
- "status": "planned",
465
+ "id": "hashnode",
466
+ "label": "Hashnode",
467
+ "status": "supported",
444
468
  "aliases": [],
445
- "postTypes": [],
469
+ "postTypes": [
470
+ "text",
471
+ "image"
472
+ ],
446
473
  "analyticsSupported": false,
447
474
  "helpers": []
448
475
  },
449
476
  {
450
- "id": "devto",
451
- "label": "Dev.to",
452
- "status": "planned",
477
+ "id": "wordpress",
478
+ "label": "WordPress",
479
+ "status": "supported",
453
480
  "aliases": [],
454
- "postTypes": [],
481
+ "postTypes": [
482
+ "text",
483
+ "image"
484
+ ],
455
485
  "analyticsSupported": false,
456
486
  "helpers": []
457
487
  },
458
488
  {
459
- "id": "hashnode",
460
- "label": "Hashnode",
489
+ "id": "lemmy",
490
+ "label": "Lemmy",
491
+ "status": "supported",
492
+ "aliases": [],
493
+ "postTypes": [
494
+ "text",
495
+ "image"
496
+ ],
497
+ "analyticsSupported": false,
498
+ "helpers": []
499
+ },
500
+ {
501
+ "id": "reddit",
502
+ "label": "Reddit",
461
503
  "status": "planned",
462
504
  "aliases": [],
463
505
  "postTypes": [],
@@ -465,8 +507,8 @@ export const PLATFORM_MANIFEST = {
465
507
  "helpers": []
466
508
  },
467
509
  {
468
- "id": "discord",
469
- "label": "Discord",
510
+ "id": "medium",
511
+ "label": "Medium",
470
512
  "status": "planned",
471
513
  "aliases": [],
472
514
  "postTypes": [],
@@ -615,50 +657,57 @@ export const PLATFORM_MANIFEST = {
615
657
  "method": "manual_credentials"
616
658
  },
617
659
  {
618
- "id": "reddit",
619
- "label": "Reddit",
620
- "status": "planned",
621
- "aliases": [],
622
- "method": "planned"
623
- },
624
- {
625
- "id": "wordpress",
626
- "label": "WordPress",
627
- "status": "planned",
660
+ "id": "discord",
661
+ "label": "Discord",
662
+ "status": "supported",
628
663
  "aliases": [],
629
- "method": "planned"
664
+ "method": "manual_credentials"
630
665
  },
631
666
  {
632
667
  "id": "mastodon",
633
668
  "label": "Mastodon",
634
- "status": "planned",
635
- "aliases": [],
636
- "method": "planned"
637
- },
638
- {
639
- "id": "medium",
640
- "label": "Medium",
641
- "status": "planned",
669
+ "status": "supported",
642
670
  "aliases": [],
643
- "method": "planned"
671
+ "method": "manual_credentials"
644
672
  },
645
673
  {
646
674
  "id": "devto",
647
675
  "label": "Dev.to",
648
- "status": "planned",
676
+ "status": "supported",
649
677
  "aliases": [],
650
- "method": "planned"
678
+ "method": "manual_credentials"
651
679
  },
652
680
  {
653
681
  "id": "hashnode",
654
682
  "label": "Hashnode",
683
+ "status": "supported",
684
+ "aliases": [],
685
+ "method": "manual_credentials"
686
+ },
687
+ {
688
+ "id": "wordpress",
689
+ "label": "WordPress",
690
+ "status": "supported",
691
+ "aliases": [],
692
+ "method": "manual_credentials"
693
+ },
694
+ {
695
+ "id": "lemmy",
696
+ "label": "Lemmy",
697
+ "status": "supported",
698
+ "aliases": [],
699
+ "method": "manual_credentials"
700
+ },
701
+ {
702
+ "id": "reddit",
703
+ "label": "Reddit",
655
704
  "status": "planned",
656
705
  "aliases": [],
657
706
  "method": "planned"
658
707
  },
659
708
  {
660
- "id": "discord",
661
- "label": "Discord",
709
+ "id": "medium",
710
+ "label": "Medium",
662
711
  "status": "planned",
663
712
  "aliases": [],
664
713
  "method": "planned"
@@ -686,12 +735,12 @@ export const PLATFORM_MANIFEST = {
686
735
  }
687
736
  ]
688
737
  };
689
- export const SUPPORTED_PLATFORM_IDS = ["instagram", "facebook", "tiktok", "twitter", "linkedin", "youtube", "pinterest", "threads", "google_business", "telegram", "bluesky"];
690
- export const PLANNED_PLATFORM_IDS = ["reddit", "wordpress", "mastodon", "medium", "devto", "hashnode", "discord", "slack", "skool", "whop"];
691
- export const ALL_PLATFORM_IDS = ["instagram", "facebook", "tiktok", "twitter", "linkedin", "youtube", "pinterest", "threads", "google_business", "telegram", "bluesky", "reddit", "wordpress", "mastodon", "medium", "devto", "hashnode", "discord", "slack", "skool", "whop"];
692
- export const SUPPORTED_PLATFORM_INPUTS = ["bluesky", "facebook", "gmb", "google-business", "google_business", "google_business_profile", "instagram", "instagram-standalone", "linkedin", "pinterest", "telegram", "threads", "tiktok", "twitter", "x", "youtube"];
693
- export const ALL_PLATFORM_INPUTS = ["bluesky", "devto", "discord", "facebook", "gmb", "google-business", "google_business", "google_business_profile", "hashnode", "instagram", "instagram-standalone", "linkedin", "mastodon", "medium", "pinterest", "reddit", "skool", "slack", "telegram", "threads", "tiktok", "twitter", "whop", "wordpress", "x", "youtube"];
694
- export const PLATFORM_HELPER_NAMES = ["linkedin.recent_mentions", "pinterest.boards", "tiktok.creator_info", "x.quota", "youtube.playlists"];
695
- export const CONNECT_TARGET_IDS = ["instagram", "meta", "facebook", "youtube", "tiktok", "linkedin", "linkedin_page", "threads", "google_business", "pinterest", "twitter", "telegram", "bluesky", "reddit", "wordpress", "mastodon", "medium", "devto", "hashnode", "discord", "slack", "skool", "whop"];
696
- export const CONNECT_INPUTS = ["bluesky", "devto", "discord", "facebook", "facebook_instagram", "facebook_pages", "gmb", "google-business", "google_business", "google_business_profile", "hashnode", "instagram", "instagram-standalone", "instagram_direct", "linkedin", "linkedin-company", "linkedin-page", "linkedin_company", "linkedin_page", "linkedin_personal", "mastodon", "medium", "meta", "meta_business", "pinterest", "reddit", "skool", "slack", "telegram", "threads", "tiktok", "twitter", "whop", "wordpress", "x", "x_twitter", "youtube"];
697
- export const ANALYTICS_PLATFORM_IDS = ["instagram", "facebook", "linkedin", "youtube", "pinterest", "google_business"];
738
+ export const SUPPORTED_PLATFORM_IDS = ["instagram", "facebook", "tiktok", "twitter", "linkedin", "youtube", "pinterest", "threads", "google_business", "telegram", "bluesky", "discord", "mastodon", "devto", "hashnode", "wordpress", "lemmy"];
739
+ export const PLANNED_PLATFORM_IDS = ["reddit", "medium", "slack", "skool", "whop"];
740
+ export const ALL_PLATFORM_IDS = ["instagram", "facebook", "tiktok", "twitter", "linkedin", "youtube", "pinterest", "threads", "google_business", "telegram", "bluesky", "discord", "mastodon", "devto", "hashnode", "wordpress", "lemmy", "reddit", "medium", "slack", "skool", "whop"];
741
+ export const SUPPORTED_PLATFORM_INPUTS = ["bluesky", "devto", "discord", "facebook", "gmb", "google-business", "google_business", "google_business_profile", "hashnode", "instagram", "instagram-standalone", "lemmy", "linkedin", "mastodon", "pinterest", "telegram", "threads", "tiktok", "twitter", "wordpress", "x", "youtube"];
742
+ export const ALL_PLATFORM_INPUTS = ["bluesky", "devto", "discord", "facebook", "gmb", "google-business", "google_business", "google_business_profile", "hashnode", "instagram", "instagram-standalone", "lemmy", "linkedin", "mastodon", "medium", "pinterest", "reddit", "skool", "slack", "telegram", "threads", "tiktok", "twitter", "whop", "wordpress", "x", "youtube"];
743
+ export const PLATFORM_HELPER_NAMES = ["linkedin.recent_mentions", "pinterest.boards", "tiktok.creator_info", "x.quota"];
744
+ export const CONNECT_TARGET_IDS = ["instagram", "meta", "facebook", "youtube", "tiktok", "linkedin", "linkedin_page", "threads", "google_business", "pinterest", "twitter", "telegram", "bluesky", "discord", "mastodon", "devto", "hashnode", "wordpress", "lemmy", "reddit", "medium", "slack", "skool", "whop"];
745
+ export const CONNECT_INPUTS = ["bluesky", "devto", "discord", "facebook", "facebook_instagram", "facebook_pages", "gmb", "google-business", "google_business", "google_business_profile", "hashnode", "instagram", "instagram-standalone", "instagram_direct", "lemmy", "linkedin", "linkedin-company", "linkedin-page", "linkedin_company", "linkedin_page", "linkedin_personal", "mastodon", "medium", "meta", "meta_business", "pinterest", "reddit", "skool", "slack", "telegram", "threads", "tiktok", "twitter", "whop", "wordpress", "x", "x_twitter", "youtube"];
746
+ export const ANALYTICS_PLATFORM_IDS = ["instagram", "facebook", "linkedin", "youtube", "pinterest", "threads", "google_business"];
package/dist/index.js CHANGED
@@ -14,6 +14,10 @@ import { getBrandTool } from './tools/get-brand.js';
14
14
  import { listBrandAccountsTool } from './tools/list-brand-accounts.js';
15
15
  import { getBrandProfileTool } from './tools/get-brand-profile.js';
16
16
  import { getLearnedVoiceTool } from './tools/get-learned-voice.js';
17
+ import { getPerformanceProfileTool } from './tools/get-performance-profile.js';
18
+ import { getPostInsightsTool } from './tools/get-post-insights.js';
19
+ import { listPostSuggestionsTool } from './tools/list-post-suggestions.js';
20
+ import { dismissSuggestionTool } from './tools/dismiss-suggestion.js';
17
21
  import { createPostTool } from './tools/create-post.js';
18
22
  import { createPostsBatchTool } from './tools/create-posts-batch.js';
19
23
  import { findSlotTool } from './tools/find-slot.js';
@@ -288,6 +292,42 @@ server.tool(getLearnedVoiceTool.name, getLearnedVoiceTool.description, getLearne
288
292
  return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
289
293
  }
290
294
  });
295
+ server.tool(getPerformanceProfileTool.name, getPerformanceProfileTool.description, getPerformanceProfileTool.inputSchema.shape, async (input) => {
296
+ try {
297
+ const text = await getPerformanceProfileTool.execute(client, input);
298
+ return { content: [{ type: 'text', text }] };
299
+ }
300
+ catch (err) {
301
+ return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
302
+ }
303
+ });
304
+ server.tool(getPostInsightsTool.name, getPostInsightsTool.description, getPostInsightsTool.inputSchema.shape, async (input) => {
305
+ try {
306
+ const text = await getPostInsightsTool.execute(client, input);
307
+ return { content: [{ type: 'text', text }] };
308
+ }
309
+ catch (err) {
310
+ return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
311
+ }
312
+ });
313
+ server.tool(listPostSuggestionsTool.name, listPostSuggestionsTool.description, listPostSuggestionsTool.inputSchema.shape, async (input) => {
314
+ try {
315
+ const text = await listPostSuggestionsTool.execute(client, input);
316
+ return { content: [{ type: 'text', text }] };
317
+ }
318
+ catch (err) {
319
+ return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
320
+ }
321
+ });
322
+ server.tool(dismissSuggestionTool.name, dismissSuggestionTool.description, dismissSuggestionTool.inputSchema.shape, async (input) => {
323
+ try {
324
+ const text = await dismissSuggestionTool.execute(client, input);
325
+ return { content: [{ type: 'text', text }] };
326
+ }
327
+ catch (err) {
328
+ return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
329
+ }
330
+ });
291
331
  server.tool(createPostTool.name, createPostTool.description, createPostTool.inputSchema.shape, async (input) => {
292
332
  try {
293
333
  const text = await createPostTool.execute(client, input);
@@ -55,6 +55,70 @@ export interface LearnedVoice {
55
55
  post_count: number;
56
56
  derived_at: string | null;
57
57
  }
58
+ export interface PerformanceProfile {
59
+ social_account_id: number;
60
+ platform: string;
61
+ username: string | null;
62
+ sample_size: number;
63
+ window_days: number;
64
+ engagement_rate_avg: number | null;
65
+ engagement_rate_trend: 'up' | 'down' | 'flat' | 'unknown';
66
+ bullets: string[];
67
+ narrative_summary: string | null;
68
+ stats: Record<string, unknown>;
69
+ derived_at: string | null;
70
+ }
71
+ export type PerformanceProfileNullReason = 'insufficient_data' | 'feature_not_available' | 'platform_not_measurable';
72
+ export interface PerformanceProfileResponse {
73
+ performance_profile: PerformanceProfile | null;
74
+ reason: PerformanceProfileNullReason | null;
75
+ }
76
+ export interface FeedbackInsight {
77
+ id: string;
78
+ post_id: number;
79
+ social_account_id: number;
80
+ username: string | null;
81
+ platform: string;
82
+ checkpoint: '1h' | '6h' | '24h' | '72h' | '7d';
83
+ performance_tier: string;
84
+ action_type: string;
85
+ title: string;
86
+ diagnosis: string;
87
+ next_action: string;
88
+ impact_score: number;
89
+ confidence: number;
90
+ metrics: Record<string, unknown>;
91
+ baseline: Record<string, unknown>;
92
+ caption: string | null;
93
+ permalink: string | null;
94
+ published_at: string | null;
95
+ created_at: string;
96
+ }
97
+ export interface FeedbackInsightsResponse {
98
+ insights: FeedbackInsight[];
99
+ }
100
+ export interface PostSuggestion {
101
+ id: string;
102
+ social_account_id: number;
103
+ platform: string;
104
+ account: {
105
+ id: number;
106
+ username: string | null;
107
+ platform: string;
108
+ };
109
+ caption: string;
110
+ rationale: string | null;
111
+ period_key: string | null;
112
+ status: string;
113
+ created_at: string;
114
+ }
115
+ export interface PostSuggestionsResponse {
116
+ suggestions: PostSuggestion[];
117
+ }
118
+ export interface DismissSuggestionResponse {
119
+ ok: boolean;
120
+ status: string;
121
+ }
58
122
  export interface Post {
59
123
  id: number;
60
124
  content: string;
@@ -697,6 +761,19 @@ export declare class PosterlyClient {
697
761
  getAccountLearnedVoice(id: string | number): Promise<{
698
762
  learned_voice: LearnedVoice | null;
699
763
  }>;
764
+ getAccountPerformanceProfile(id: string | number): Promise<PerformanceProfileResponse>;
765
+ getPostInsights(params?: {
766
+ account_id?: string | number;
767
+ post_id?: number;
768
+ checkpoint?: '1h' | '6h' | '24h' | '72h' | '7d';
769
+ limit?: number;
770
+ }): Promise<FeedbackInsightsResponse>;
771
+ listPostSuggestions(params?: {
772
+ account_id?: string | number;
773
+ status?: 'pending' | 'scheduled' | 'dismissed';
774
+ limit?: number;
775
+ }): Promise<PostSuggestionsResponse>;
776
+ dismissSuggestion(id: string): Promise<DismissSuggestionResponse>;
700
777
  listPosts(params?: {
701
778
  status?: string;
702
779
  platform?: string;
@@ -967,3 +1044,16 @@ export declare class PosterlyClient {
967
1044
  }>;
968
1045
  }
969
1046
  export declare function resolvePosterlyBaseUrl(baseUrl?: string): string;
1047
+ type PosterlyBaseUrlInspection = {
1048
+ valid: boolean;
1049
+ secure: boolean;
1050
+ protocol?: string;
1051
+ host?: string;
1052
+ trustedPosterlyHost: boolean;
1053
+ localDevelopmentHost: boolean;
1054
+ warnings: string[];
1055
+ message: string;
1056
+ };
1057
+ export declare function inspectPosterlyBaseUrl(baseUrl: string): PosterlyBaseUrlInspection;
1058
+ export declare function validatePosterlyBaseUrlForKeyUse(baseUrl: string): void;
1059
+ export {};
@@ -6,6 +6,9 @@ export class PosterlyClient {
6
6
  constructor(apiKey, baseUrl) {
7
7
  this.apiKey = apiKey || process.env.POSTERLY_API_KEY || '';
8
8
  this.baseUrl = resolvePosterlyBaseUrl(baseUrl);
9
+ if (this.apiKey) {
10
+ validatePosterlyBaseUrlForKeyUse(this.baseUrl);
11
+ }
9
12
  }
10
13
  hasApiKey() {
11
14
  return Boolean(this.apiKey);
@@ -182,6 +185,36 @@ export class PosterlyClient {
182
185
  async getAccountLearnedVoice(id) {
183
186
  return this.request('GET', `/accounts/${encodeURIComponent(String(id))}/learned-voice`);
184
187
  }
188
+ async getAccountPerformanceProfile(id) {
189
+ return this.request('GET', `/accounts/${encodeURIComponent(String(id))}/performance-profile`);
190
+ }
191
+ async getPostInsights(params) {
192
+ const searchParams = new URLSearchParams();
193
+ if (params?.account_id)
194
+ searchParams.set('account_id', String(params.account_id));
195
+ if (params?.post_id)
196
+ searchParams.set('post_id', String(params.post_id));
197
+ if (params?.checkpoint)
198
+ searchParams.set('checkpoint', params.checkpoint);
199
+ if (params?.limit)
200
+ searchParams.set('limit', String(params.limit));
201
+ const qs = searchParams.toString();
202
+ return this.request('GET', `/analytics/insights${qs ? `?${qs}` : ''}`);
203
+ }
204
+ async listPostSuggestions(params) {
205
+ const searchParams = new URLSearchParams();
206
+ if (params?.account_id)
207
+ searchParams.set('account_id', String(params.account_id));
208
+ if (params?.status)
209
+ searchParams.set('status', params.status);
210
+ if (params?.limit)
211
+ searchParams.set('limit', String(params.limit));
212
+ const qs = searchParams.toString();
213
+ return this.request('GET', `/suggestions${qs ? `?${qs}` : ''}`);
214
+ }
215
+ async dismissSuggestion(id) {
216
+ return this.request('POST', `/suggestions/${encodeURIComponent(id)}/dismiss`, {});
217
+ }
185
218
  async listPosts(params) {
186
219
  const searchParams = new URLSearchParams();
187
220
  if (params?.status)
@@ -485,6 +518,73 @@ export function resolvePosterlyBaseUrl(baseUrl) {
485
518
  return configured.replace(/\/api\/v1\/?$/, '').replace(/\/$/, '');
486
519
  }
487
520
  }
521
+ const TRUSTED_POSTERLY_HOSTS = new Set(['poster.ly', 'www.poster.ly']);
522
+ function flagEnabled(name) {
523
+ const value = (process.env[name] || '').trim().toLowerCase();
524
+ return value === 'true' || value === '1' || value === 'yes';
525
+ }
526
+ function normalizeHostname(hostname) {
527
+ return hostname.toLowerCase().replace(/^\[|\]$/g, '');
528
+ }
529
+ function isLocalHost(hostname) {
530
+ const normalized = normalizeHostname(hostname);
531
+ return (normalized === 'localhost' ||
532
+ normalized.endsWith('.localhost') ||
533
+ normalized === '127.0.0.1' ||
534
+ normalized === '::1');
535
+ }
536
+ export function inspectPosterlyBaseUrl(baseUrl) {
537
+ try {
538
+ const url = new URL(baseUrl);
539
+ const hostname = normalizeHostname(url.hostname);
540
+ const local = isLocalHost(hostname);
541
+ const trustedPosterlyHost = TRUSTED_POSTERLY_HOSTS.has(hostname);
542
+ const secure = url.protocol === 'https:' || (url.protocol === 'http:' && local);
543
+ const warnings = [];
544
+ if (!trustedPosterlyHost) {
545
+ warnings.push(local ? 'local_development_host' : 'custom_host');
546
+ }
547
+ if (!secure) {
548
+ warnings.push('insecure_non_local_url');
549
+ }
550
+ return {
551
+ valid: true,
552
+ secure,
553
+ protocol: url.protocol.replace(/:$/, ''),
554
+ host: url.host,
555
+ trustedPosterlyHost,
556
+ localDevelopmentHost: local,
557
+ warnings,
558
+ message: secure
559
+ ? 'Base URL is secure for API-key requests.'
560
+ : 'Non-local HTTP URLs are blocked for API-key requests.',
561
+ };
562
+ }
563
+ catch {
564
+ return {
565
+ valid: false,
566
+ secure: false,
567
+ trustedPosterlyHost: false,
568
+ localDevelopmentHost: false,
569
+ warnings: ['invalid_url'],
570
+ message: 'Base URL must include a scheme and host, for example https://www.poster.ly.',
571
+ };
572
+ }
573
+ }
574
+ export function validatePosterlyBaseUrlForKeyUse(baseUrl) {
575
+ const check = inspectPosterlyBaseUrl(baseUrl);
576
+ if (!check.valid) {
577
+ throw new Error(`${check.message} Received: ${baseUrl}`);
578
+ }
579
+ if (!check.secure && !flagEnabled('POSTERLY_ALLOW_INSECURE_URL')) {
580
+ throw new Error(`Refusing to send an API key to ${baseUrl}. Use https, http://localhost for local development, or set POSTERLY_ALLOW_INSECURE_URL=true only for a trusted private development endpoint.`);
581
+ }
582
+ if (!check.trustedPosterlyHost &&
583
+ !check.localDevelopmentHost &&
584
+ !flagEnabled('POSTERLY_ALLOW_CUSTOM_BASE_URL')) {
585
+ throw new Error(`Refusing to send an API key to custom host ${baseUrl}. Set POSTERLY_ALLOW_CUSTOM_BASE_URL=true only for a trusted Posterly deployment.`);
586
+ }
587
+ }
488
588
  function parseJson(text) {
489
589
  try {
490
590
  return text ? JSON.parse(text) : {};
@@ -1 +1 @@
1
- export declare const POSTERLY_MCP_VERSION = "0.23.1";
1
+ export declare const POSTERLY_MCP_VERSION = "0.24.0";
@@ -5,4 +5,4 @@
5
5
  // tool set (minus the intentional pre-auth signup tools that only this stdio
6
6
  // package exposes). `npm run check:mcp-parity` enforces both the version match
7
7
  // and the tool-list match, and runs in the pre-commit hook.
8
- export const POSTERLY_MCP_VERSION = '0.23.1';
8
+ export const POSTERLY_MCP_VERSION = '0.24.0';