posterly-mcp-server 0.8.0 → 0.8.2
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 +3 -1
- package/dist/lib/api-client.d.ts +8 -0
- package/dist/tools/create-post.d.ts +24 -0
- package/dist/tools/create-post.js +4 -1
- package/dist/tools/get-account-analytics.js +17 -1
- package/dist/tools/get-post-analytics.js +17 -1
- package/dist/tools/update-post.d.ts +24 -0
- package/dist/tools/update-post.js +3 -0
- package/package.json +2 -2
- package/src/lib/api-client.ts +8 -0
- package/src/tools/create-post.ts +4 -1
- package/src/tools/get-account-analytics.ts +15 -1
- package/src/tools/get-post-analytics.ts +21 -1
- package/src/tools/update-post.ts +3 -0
package/README.md
CHANGED
|
@@ -93,7 +93,7 @@ Add the same server definition to your Cursor MCP settings:
|
|
|
93
93
|
|
|
94
94
|
## Available tools
|
|
95
95
|
|
|
96
|
-
`posterly-mcp-server@0.
|
|
96
|
+
`posterly-mcp-server@0.8.2` exposes 16 tools:
|
|
97
97
|
|
|
98
98
|
- `whoami`
|
|
99
99
|
- `list_accounts`
|
|
@@ -112,6 +112,8 @@ Add the same server definition to your Cursor MCP settings:
|
|
|
112
112
|
- `get_account_analytics`
|
|
113
113
|
- `get_post_analytics`
|
|
114
114
|
|
|
115
|
+
Analytics tools currently support Instagram, LinkedIn, Google Business Profile, Pinterest, and YouTube.
|
|
116
|
+
|
|
115
117
|
## What the brand tools are for
|
|
116
118
|
|
|
117
119
|
Posterly workspaces often have multiple connected accounts under one client or brand.
|
package/dist/lib/api-client.d.ts
CHANGED
|
@@ -108,6 +108,7 @@ export interface AccountAnalyticsSummary {
|
|
|
108
108
|
followers_change: number;
|
|
109
109
|
total_reach: number | null;
|
|
110
110
|
total_views: number | null;
|
|
111
|
+
total_profile_views?: number | null;
|
|
111
112
|
total_accounts_engaged: number | null;
|
|
112
113
|
total_follows_gained: number;
|
|
113
114
|
total_follows_lost: number;
|
|
@@ -118,6 +119,8 @@ export interface AccountAnalyticsSummary {
|
|
|
118
119
|
total_direction_requests: number | null;
|
|
119
120
|
total_conversations: number | null;
|
|
120
121
|
total_bookings: number | null;
|
|
122
|
+
total_watch_minutes?: number | null;
|
|
123
|
+
total_likes?: number | null;
|
|
121
124
|
}
|
|
122
125
|
export interface AccountAnalyticsSnapshot {
|
|
123
126
|
date: string;
|
|
@@ -162,6 +165,11 @@ export interface PostAnalyticsRow {
|
|
|
162
165
|
shares: number;
|
|
163
166
|
plays: number;
|
|
164
167
|
total_interactions: number;
|
|
168
|
+
avg_watch_time_ms?: number | null;
|
|
169
|
+
total_watch_time_ms?: number | null;
|
|
170
|
+
url_link_clicks?: number;
|
|
171
|
+
user_profile_clicks?: number;
|
|
172
|
+
engagements?: number;
|
|
165
173
|
media_type: string | null;
|
|
166
174
|
media_url: string | null;
|
|
167
175
|
permalink: string | null;
|
|
@@ -115,6 +115,9 @@ declare const platformSettingsSchema: z.ZodObject<{
|
|
|
115
115
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
116
116
|
board_id: z.ZodOptional<z.ZodString>;
|
|
117
117
|
link: z.ZodOptional<z.ZodString>;
|
|
118
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
119
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
120
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
118
121
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
119
122
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
120
123
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -160,6 +163,9 @@ declare const platformSettingsSchema: z.ZodObject<{
|
|
|
160
163
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
161
164
|
board_id: z.ZodOptional<z.ZodString>;
|
|
162
165
|
link: z.ZodOptional<z.ZodString>;
|
|
166
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
167
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
168
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
163
169
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
164
170
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
165
171
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -205,6 +211,9 @@ declare const platformSettingsSchema: z.ZodObject<{
|
|
|
205
211
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
206
212
|
board_id: z.ZodOptional<z.ZodString>;
|
|
207
213
|
link: z.ZodOptional<z.ZodString>;
|
|
214
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
215
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
216
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
208
217
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
209
218
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
210
219
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -338,6 +347,9 @@ export declare const createPostTool: {
|
|
|
338
347
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
339
348
|
board_id: z.ZodOptional<z.ZodString>;
|
|
340
349
|
link: z.ZodOptional<z.ZodString>;
|
|
350
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
351
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
352
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
341
353
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
342
354
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
343
355
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -383,6 +395,9 @@ export declare const createPostTool: {
|
|
|
383
395
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
384
396
|
board_id: z.ZodOptional<z.ZodString>;
|
|
385
397
|
link: z.ZodOptional<z.ZodString>;
|
|
398
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
399
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
400
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
386
401
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
387
402
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
388
403
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -428,6 +443,9 @@ export declare const createPostTool: {
|
|
|
428
443
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
429
444
|
board_id: z.ZodOptional<z.ZodString>;
|
|
430
445
|
link: z.ZodOptional<z.ZodString>;
|
|
446
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
447
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
448
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
431
449
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
432
450
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
433
451
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -506,6 +524,9 @@ export declare const createPostTool: {
|
|
|
506
524
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
507
525
|
board_id: z.ZodOptional<z.ZodString>;
|
|
508
526
|
link: z.ZodOptional<z.ZodString>;
|
|
527
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
528
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
529
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
509
530
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
510
531
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
511
532
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -583,6 +604,9 @@ export declare const createPostTool: {
|
|
|
583
604
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
584
605
|
board_id: z.ZodOptional<z.ZodString>;
|
|
585
606
|
link: z.ZodOptional<z.ZodString>;
|
|
607
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
608
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
609
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
586
610
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
587
611
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
588
612
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -57,6 +57,9 @@ const platformSettingsSchema = z.object({
|
|
|
57
57
|
// Pinterest
|
|
58
58
|
board_id: z.string().optional(),
|
|
59
59
|
link: z.string().optional(),
|
|
60
|
+
cover_image_url: z.string().optional(),
|
|
61
|
+
video_cover_url: z.string().optional(),
|
|
62
|
+
cover_image_method: z.enum(['upload', 'frame', 'api']).optional(),
|
|
60
63
|
// Google Business
|
|
61
64
|
event: z.record(z.unknown()).optional(),
|
|
62
65
|
offer: z.record(z.unknown()).optional(),
|
|
@@ -74,7 +77,7 @@ export const createPostTool = {
|
|
|
74
77
|
'4. If scheduling multiple posts in one turn, list every post first and confirm the batch as a whole before calling create_post repeatedly.\n\n' +
|
|
75
78
|
'Provide either account_id OR username+platform to identify the account. If scheduled_at is omitted, the post publishes immediately. If workspace_id is omitted, the server resolves one from the social account, falling back to the caller\'s default (personal) workspace — pass workspace_id explicitly if the user has more than one workspace.\n\n' +
|
|
76
79
|
'THREADS: Pass `thread_posts` (an array of 2+ strings) to schedule a multi-post thread on X (Twitter) or Threads (Meta). The first entry is the lead post; the rest are published as replies in the same chain. X entries are capped at 280 characters each (4000 for verified, 25000 for organization accounts); Threads entries are capped at 500 characters each. When `thread_posts` is set, `caption` is ignored.\n\n' +
|
|
77
|
-
'PLATFORM SETTINGS: Pass `platform_settings` for composer-equivalent controls: Facebook story/reel/backgrounds, YouTube title/privacy/thumbnail/playlist, LinkedIn document titles/mentions/video thumbnails, TikTok direct-post privacy/toggles/commercial disclosure, Pinterest board/link/title, GBP event/offer/CTA, X reply settings/polls, Threads reply controls/text attachments, Bluesky languages/alt text, and Instagram feed/story/reel/carousel/collaborators/first comments/trial Reels. `instagram_settings` remains as a backwards-compatible alias.',
|
|
80
|
+
'PLATFORM SETTINGS: Pass `platform_settings` for composer-equivalent controls: Facebook story/reel/backgrounds, YouTube title/privacy/thumbnail/playlist, LinkedIn document titles/mentions/video thumbnails, TikTok direct-post privacy/toggles/commercial disclosure, Pinterest board/link/title/video cover, GBP event/offer/CTA, X reply settings/polls, Threads reply controls/text attachments, Bluesky languages/alt text, and Instagram feed/story/reel/carousel/collaborators/first comments/trial Reels. `instagram_settings` remains as a backwards-compatible alias.',
|
|
78
81
|
inputSchema: z.object({
|
|
79
82
|
account_id: z.string().optional().describe('Social account ID (from list_accounts)'),
|
|
80
83
|
username: z.string().optional().describe('Account username (alternative to account_id)'),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export const getAccountAnalyticsTool = {
|
|
3
3
|
name: 'get_account_analytics',
|
|
4
|
-
description: 'Get daily analytics snapshots and a period summary for a connected social account. Supports Instagram, LinkedIn,
|
|
4
|
+
description: 'Get daily analytics snapshots and a period summary for a connected social account. Supports Instagram, LinkedIn, Google Business Profile, Pinterest, and YouTube. Returns follower growth, reach, views, engagement rate, and platform-specific metrics.',
|
|
5
5
|
inputSchema: z.object({
|
|
6
6
|
account_id: z
|
|
7
7
|
.number()
|
|
@@ -30,6 +30,9 @@ export const getAccountAnalyticsTool = {
|
|
|
30
30
|
lines.push(`• Total reach: ${summary.total_reach.toLocaleString()}`);
|
|
31
31
|
if (summary.total_views !== null)
|
|
32
32
|
lines.push(`• Total views: ${summary.total_views.toLocaleString()}`);
|
|
33
|
+
if (summary.total_profile_views != null) {
|
|
34
|
+
lines.push(`• Profile views: ${summary.total_profile_views.toLocaleString()}`);
|
|
35
|
+
}
|
|
33
36
|
if (summary.total_accounts_engaged !== null) {
|
|
34
37
|
lines.push(`• Total accounts engaged: ${summary.total_accounts_engaged.toLocaleString()}`);
|
|
35
38
|
}
|
|
@@ -51,6 +54,19 @@ export const getAccountAnalyticsTool = {
|
|
|
51
54
|
lines.push(`• Bookings: ${summary.total_bookings.toLocaleString()}`);
|
|
52
55
|
}
|
|
53
56
|
}
|
|
57
|
+
else if (account.platform === 'pinterest') {
|
|
58
|
+
if (summary.total_website_clicks !== null) {
|
|
59
|
+
lines.push(`• Outbound clicks: ${summary.total_website_clicks.toLocaleString()}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else if (account.platform === 'youtube') {
|
|
63
|
+
if (summary.total_watch_minutes != null) {
|
|
64
|
+
lines.push(`• Watch minutes: ${summary.total_watch_minutes.toLocaleString()}`);
|
|
65
|
+
}
|
|
66
|
+
if (summary.total_likes != null) {
|
|
67
|
+
lines.push(`• Likes: ${summary.total_likes.toLocaleString()}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
54
70
|
return lines.join('\n');
|
|
55
71
|
},
|
|
56
72
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export const getPostAnalyticsTool = {
|
|
3
3
|
name: 'get_post_analytics',
|
|
4
|
-
description: 'Get per-post engagement metrics (likes, comments, reach, impressions, saves, shares, plays) for a connected social account. Supports Instagram, LinkedIn,
|
|
4
|
+
description: 'Get per-post engagement metrics (likes, comments, reach, impressions, saves, shares, plays, clicks, watch time) for a connected social account. Supports Instagram, LinkedIn, Google Business Profile, Pinterest, and YouTube. Returns the most recent posts first.',
|
|
5
5
|
inputSchema: z.object({
|
|
6
6
|
account_id: z
|
|
7
7
|
.number()
|
|
@@ -55,6 +55,22 @@ export const getPostAnalyticsTool = {
|
|
|
55
55
|
metrics.push(`${p.shares} shares`);
|
|
56
56
|
if (p.plays)
|
|
57
57
|
metrics.push(`${p.plays.toLocaleString()} plays`);
|
|
58
|
+
if (account.platform === 'youtube' && p.total_watch_time_ms) {
|
|
59
|
+
metrics.push(`${Math.round(p.total_watch_time_ms / 60000).toLocaleString()} watch minutes`);
|
|
60
|
+
}
|
|
61
|
+
if (account.platform === 'youtube' && p.avg_watch_time_ms) {
|
|
62
|
+
metrics.push(`${Math.round(p.avg_watch_time_ms / 1000)}s avg view`);
|
|
63
|
+
}
|
|
64
|
+
if (p.url_link_clicks) {
|
|
65
|
+
metrics.push(account.platform === 'pinterest'
|
|
66
|
+
? `${p.url_link_clicks.toLocaleString()} outbound clicks`
|
|
67
|
+
: `${p.url_link_clicks.toLocaleString()} link clicks`);
|
|
68
|
+
}
|
|
69
|
+
if (p.user_profile_clicks) {
|
|
70
|
+
metrics.push(account.platform === 'pinterest'
|
|
71
|
+
? `${p.user_profile_clicks.toLocaleString()} pin clicks`
|
|
72
|
+
: `${p.user_profile_clicks.toLocaleString()} profile clicks`);
|
|
73
|
+
}
|
|
58
74
|
lines.push(`• [${postedAt}] ${caption}`);
|
|
59
75
|
lines.push(` ${metrics.join(' • ')}`);
|
|
60
76
|
if (p.permalink)
|
|
@@ -115,6 +115,9 @@ declare const platformSettingsSchema: z.ZodObject<{
|
|
|
115
115
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
116
116
|
board_id: z.ZodOptional<z.ZodString>;
|
|
117
117
|
link: z.ZodOptional<z.ZodString>;
|
|
118
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
119
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
120
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
118
121
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
119
122
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
120
123
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -160,6 +163,9 @@ declare const platformSettingsSchema: z.ZodObject<{
|
|
|
160
163
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
161
164
|
board_id: z.ZodOptional<z.ZodString>;
|
|
162
165
|
link: z.ZodOptional<z.ZodString>;
|
|
166
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
167
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
168
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
163
169
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
164
170
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
165
171
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -205,6 +211,9 @@ declare const platformSettingsSchema: z.ZodObject<{
|
|
|
205
211
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
206
212
|
board_id: z.ZodOptional<z.ZodString>;
|
|
207
213
|
link: z.ZodOptional<z.ZodString>;
|
|
214
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
215
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
216
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
208
217
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
209
218
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
210
219
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -335,6 +344,9 @@ export declare const updatePostTool: {
|
|
|
335
344
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
336
345
|
board_id: z.ZodOptional<z.ZodString>;
|
|
337
346
|
link: z.ZodOptional<z.ZodString>;
|
|
347
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
348
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
349
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
338
350
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
339
351
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
340
352
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -380,6 +392,9 @@ export declare const updatePostTool: {
|
|
|
380
392
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
381
393
|
board_id: z.ZodOptional<z.ZodString>;
|
|
382
394
|
link: z.ZodOptional<z.ZodString>;
|
|
395
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
396
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
397
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
383
398
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
384
399
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
385
400
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -425,6 +440,9 @@ export declare const updatePostTool: {
|
|
|
425
440
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
426
441
|
board_id: z.ZodOptional<z.ZodString>;
|
|
427
442
|
link: z.ZodOptional<z.ZodString>;
|
|
443
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
444
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
445
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
428
446
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
429
447
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
430
448
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -498,6 +516,9 @@ export declare const updatePostTool: {
|
|
|
498
516
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
499
517
|
board_id: z.ZodOptional<z.ZodString>;
|
|
500
518
|
link: z.ZodOptional<z.ZodString>;
|
|
519
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
520
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
521
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
501
522
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
502
523
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
503
524
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -571,6 +592,9 @@ export declare const updatePostTool: {
|
|
|
571
592
|
playlist_id: z.ZodOptional<z.ZodString>;
|
|
572
593
|
board_id: z.ZodOptional<z.ZodString>;
|
|
573
594
|
link: z.ZodOptional<z.ZodString>;
|
|
595
|
+
cover_image_url: z.ZodOptional<z.ZodString>;
|
|
596
|
+
video_cover_url: z.ZodOptional<z.ZodString>;
|
|
597
|
+
cover_image_method: z.ZodOptional<z.ZodEnum<["upload", "frame", "api"]>>;
|
|
574
598
|
event: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
575
599
|
offer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
576
600
|
cta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -47,6 +47,9 @@ const platformSettingsSchema = z.object({
|
|
|
47
47
|
playlist_id: z.string().optional(),
|
|
48
48
|
board_id: z.string().optional(),
|
|
49
49
|
link: z.string().optional(),
|
|
50
|
+
cover_image_url: z.string().optional(),
|
|
51
|
+
video_cover_url: z.string().optional(),
|
|
52
|
+
cover_image_method: z.enum(['upload', 'frame', 'api']).optional(),
|
|
50
53
|
event: z.record(z.unknown()).optional(),
|
|
51
54
|
offer: z.record(z.unknown()).optional(),
|
|
52
55
|
cta: z.record(z.unknown()).optional(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "posterly-mcp-server",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "MCP server for posterly — schedule social media posts from Claude Desktop",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://www.poster.ly/mcp",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
],
|
|
25
25
|
"type": "module",
|
|
26
26
|
"bin": {
|
|
27
|
-
"posterly-mcp": "
|
|
27
|
+
"posterly-mcp": "dist/index.js"
|
|
28
28
|
},
|
|
29
29
|
"main": "./dist/index.js",
|
|
30
30
|
"scripts": {
|
package/src/lib/api-client.ts
CHANGED
|
@@ -106,6 +106,7 @@ export interface AccountAnalyticsSummary {
|
|
|
106
106
|
followers_change: number;
|
|
107
107
|
total_reach: number | null;
|
|
108
108
|
total_views: number | null;
|
|
109
|
+
total_profile_views?: number | null;
|
|
109
110
|
total_accounts_engaged: number | null;
|
|
110
111
|
total_follows_gained: number;
|
|
111
112
|
total_follows_lost: number;
|
|
@@ -116,6 +117,8 @@ export interface AccountAnalyticsSummary {
|
|
|
116
117
|
total_direction_requests: number | null;
|
|
117
118
|
total_conversations: number | null;
|
|
118
119
|
total_bookings: number | null;
|
|
120
|
+
total_watch_minutes?: number | null;
|
|
121
|
+
total_likes?: number | null;
|
|
119
122
|
}
|
|
120
123
|
|
|
121
124
|
export interface AccountAnalyticsSnapshot {
|
|
@@ -156,6 +159,11 @@ export interface PostAnalyticsRow {
|
|
|
156
159
|
shares: number;
|
|
157
160
|
plays: number;
|
|
158
161
|
total_interactions: number;
|
|
162
|
+
avg_watch_time_ms?: number | null;
|
|
163
|
+
total_watch_time_ms?: number | null;
|
|
164
|
+
url_link_clicks?: number;
|
|
165
|
+
user_profile_clicks?: number;
|
|
166
|
+
engagements?: number;
|
|
159
167
|
media_type: string | null;
|
|
160
168
|
media_url: string | null;
|
|
161
169
|
permalink: string | null;
|
package/src/tools/create-post.ts
CHANGED
|
@@ -60,6 +60,9 @@ const platformSettingsSchema = z.object({
|
|
|
60
60
|
// Pinterest
|
|
61
61
|
board_id: z.string().optional(),
|
|
62
62
|
link: z.string().optional(),
|
|
63
|
+
cover_image_url: z.string().optional(),
|
|
64
|
+
video_cover_url: z.string().optional(),
|
|
65
|
+
cover_image_method: z.enum(['upload', 'frame', 'api']).optional(),
|
|
63
66
|
// Google Business
|
|
64
67
|
event: z.record(z.unknown()).optional(),
|
|
65
68
|
offer: z.record(z.unknown()).optional(),
|
|
@@ -79,7 +82,7 @@ export const createPostTool = {
|
|
|
79
82
|
'4. If scheduling multiple posts in one turn, list every post first and confirm the batch as a whole before calling create_post repeatedly.\n\n' +
|
|
80
83
|
'Provide either account_id OR username+platform to identify the account. If scheduled_at is omitted, the post publishes immediately. If workspace_id is omitted, the server resolves one from the social account, falling back to the caller\'s default (personal) workspace — pass workspace_id explicitly if the user has more than one workspace.\n\n' +
|
|
81
84
|
'THREADS: Pass `thread_posts` (an array of 2+ strings) to schedule a multi-post thread on X (Twitter) or Threads (Meta). The first entry is the lead post; the rest are published as replies in the same chain. X entries are capped at 280 characters each (4000 for verified, 25000 for organization accounts); Threads entries are capped at 500 characters each. When `thread_posts` is set, `caption` is ignored.\n\n' +
|
|
82
|
-
'PLATFORM SETTINGS: Pass `platform_settings` for composer-equivalent controls: Facebook story/reel/backgrounds, YouTube title/privacy/thumbnail/playlist, LinkedIn document titles/mentions/video thumbnails, TikTok direct-post privacy/toggles/commercial disclosure, Pinterest board/link/title, GBP event/offer/CTA, X reply settings/polls, Threads reply controls/text attachments, Bluesky languages/alt text, and Instagram feed/story/reel/carousel/collaborators/first comments/trial Reels. `instagram_settings` remains as a backwards-compatible alias.',
|
|
85
|
+
'PLATFORM SETTINGS: Pass `platform_settings` for composer-equivalent controls: Facebook story/reel/backgrounds, YouTube title/privacy/thumbnail/playlist, LinkedIn document titles/mentions/video thumbnails, TikTok direct-post privacy/toggles/commercial disclosure, Pinterest board/link/title/video cover, GBP event/offer/CTA, X reply settings/polls, Threads reply controls/text attachments, Bluesky languages/alt text, and Instagram feed/story/reel/carousel/collaborators/first comments/trial Reels. `instagram_settings` remains as a backwards-compatible alias.',
|
|
83
86
|
inputSchema: z.object({
|
|
84
87
|
account_id: z.string().optional().describe('Social account ID (from list_accounts)'),
|
|
85
88
|
username: z.string().optional().describe('Account username (alternative to account_id)'),
|
|
@@ -4,7 +4,7 @@ import type { PosterlyClient } from '../lib/api-client.js';
|
|
|
4
4
|
export const getAccountAnalyticsTool = {
|
|
5
5
|
name: 'get_account_analytics',
|
|
6
6
|
description:
|
|
7
|
-
'Get daily analytics snapshots and a period summary for a connected social account. Supports Instagram, LinkedIn,
|
|
7
|
+
'Get daily analytics snapshots and a period summary for a connected social account. Supports Instagram, LinkedIn, Google Business Profile, Pinterest, and YouTube. Returns follower growth, reach, views, engagement rate, and platform-specific metrics.',
|
|
8
8
|
inputSchema: z.object({
|
|
9
9
|
account_id: z
|
|
10
10
|
.number()
|
|
@@ -37,6 +37,9 @@ export const getAccountAnalyticsTool = {
|
|
|
37
37
|
|
|
38
38
|
if (summary.total_reach !== null) lines.push(`• Total reach: ${summary.total_reach.toLocaleString()}`);
|
|
39
39
|
if (summary.total_views !== null) lines.push(`• Total views: ${summary.total_views.toLocaleString()}`);
|
|
40
|
+
if (summary.total_profile_views != null) {
|
|
41
|
+
lines.push(`• Profile views: ${summary.total_profile_views.toLocaleString()}`);
|
|
42
|
+
}
|
|
40
43
|
if (summary.total_accounts_engaged !== null) {
|
|
41
44
|
lines.push(`• Total accounts engaged: ${summary.total_accounts_engaged.toLocaleString()}`);
|
|
42
45
|
}
|
|
@@ -61,6 +64,17 @@ export const getAccountAnalyticsTool = {
|
|
|
61
64
|
if (summary.total_bookings !== null) {
|
|
62
65
|
lines.push(`• Bookings: ${summary.total_bookings.toLocaleString()}`);
|
|
63
66
|
}
|
|
67
|
+
} else if (account.platform === 'pinterest') {
|
|
68
|
+
if (summary.total_website_clicks !== null) {
|
|
69
|
+
lines.push(`• Outbound clicks: ${summary.total_website_clicks.toLocaleString()}`);
|
|
70
|
+
}
|
|
71
|
+
} else if (account.platform === 'youtube') {
|
|
72
|
+
if (summary.total_watch_minutes != null) {
|
|
73
|
+
lines.push(`• Watch minutes: ${summary.total_watch_minutes.toLocaleString()}`);
|
|
74
|
+
}
|
|
75
|
+
if (summary.total_likes != null) {
|
|
76
|
+
lines.push(`• Likes: ${summary.total_likes.toLocaleString()}`);
|
|
77
|
+
}
|
|
64
78
|
}
|
|
65
79
|
|
|
66
80
|
return lines.join('\n');
|
|
@@ -4,7 +4,7 @@ import type { PosterlyClient } from '../lib/api-client.js';
|
|
|
4
4
|
export const getPostAnalyticsTool = {
|
|
5
5
|
name: 'get_post_analytics',
|
|
6
6
|
description:
|
|
7
|
-
'Get per-post engagement metrics (likes, comments, reach, impressions, saves, shares, plays) for a connected social account. Supports Instagram, LinkedIn,
|
|
7
|
+
'Get per-post engagement metrics (likes, comments, reach, impressions, saves, shares, plays, clicks, watch time) for a connected social account. Supports Instagram, LinkedIn, Google Business Profile, Pinterest, and YouTube. Returns the most recent posts first.',
|
|
8
8
|
inputSchema: z.object({
|
|
9
9
|
account_id: z
|
|
10
10
|
.number()
|
|
@@ -67,6 +67,26 @@ export const getPostAnalyticsTool = {
|
|
|
67
67
|
if (p.saved) metrics.push(`${p.saved} saved`);
|
|
68
68
|
if (p.shares) metrics.push(`${p.shares} shares`);
|
|
69
69
|
if (p.plays) metrics.push(`${p.plays.toLocaleString()} plays`);
|
|
70
|
+
if (account.platform === 'youtube' && p.total_watch_time_ms) {
|
|
71
|
+
metrics.push(`${Math.round(p.total_watch_time_ms / 60000).toLocaleString()} watch minutes`);
|
|
72
|
+
}
|
|
73
|
+
if (account.platform === 'youtube' && p.avg_watch_time_ms) {
|
|
74
|
+
metrics.push(`${Math.round(p.avg_watch_time_ms / 1000)}s avg view`);
|
|
75
|
+
}
|
|
76
|
+
if (p.url_link_clicks) {
|
|
77
|
+
metrics.push(
|
|
78
|
+
account.platform === 'pinterest'
|
|
79
|
+
? `${p.url_link_clicks.toLocaleString()} outbound clicks`
|
|
80
|
+
: `${p.url_link_clicks.toLocaleString()} link clicks`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (p.user_profile_clicks) {
|
|
84
|
+
metrics.push(
|
|
85
|
+
account.platform === 'pinterest'
|
|
86
|
+
? `${p.user_profile_clicks.toLocaleString()} pin clicks`
|
|
87
|
+
: `${p.user_profile_clicks.toLocaleString()} profile clicks`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
70
90
|
|
|
71
91
|
lines.push(`• [${postedAt}] ${caption}`);
|
|
72
92
|
lines.push(` ${metrics.join(' • ')}`);
|
package/src/tools/update-post.ts
CHANGED
|
@@ -50,6 +50,9 @@ const platformSettingsSchema = z.object({
|
|
|
50
50
|
playlist_id: z.string().optional(),
|
|
51
51
|
board_id: z.string().optional(),
|
|
52
52
|
link: z.string().optional(),
|
|
53
|
+
cover_image_url: z.string().optional(),
|
|
54
|
+
video_cover_url: z.string().optional(),
|
|
55
|
+
cover_image_method: z.enum(['upload', 'frame', 'api']).optional(),
|
|
53
56
|
event: z.record(z.unknown()).optional(),
|
|
54
57
|
offer: z.record(z.unknown()).optional(),
|
|
55
58
|
cta: z.record(z.unknown()).optional(),
|