posterly-mcp-server 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.7.0` exposes 16 tools:
96
+ `posterly-mcp-server@0.8.1` 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, and Pinterest.
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.
@@ -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;
@@ -162,6 +163,8 @@ export interface PostAnalyticsRow {
162
163
  shares: number;
163
164
  plays: number;
164
165
  total_interactions: number;
166
+ url_link_clicks?: number;
167
+ user_profile_clicks?: number;
165
168
  media_type: string | null;
166
169
  media_url: string | null;
167
170
  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, and Google Business Profile. Returns follower growth, reach, views, engagement rate, and platform-specific metrics (e.g. website clicks, direction requests for GBP).',
4
+ description: 'Get daily analytics snapshots and a period summary for a connected social account. Supports Instagram, LinkedIn, Google Business Profile, and Pinterest. 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,11 @@ 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
+ }
54
62
  return lines.join('\n');
55
63
  },
56
64
  };
@@ -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, and Google Business Profile. Returns the most recent posts first.',
4
+ description: 'Get per-post engagement metrics (likes, comments, reach, impressions, saves, shares, plays, clicks) for a connected social account. Supports Instagram, LinkedIn, Google Business Profile, and Pinterest. Returns the most recent posts first.',
5
5
  inputSchema: z.object({
6
6
  account_id: z
7
7
  .number()
@@ -55,6 +55,16 @@ 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 (p.url_link_clicks) {
59
+ metrics.push(account.platform === 'pinterest'
60
+ ? `${p.url_link_clicks.toLocaleString()} outbound clicks`
61
+ : `${p.url_link_clicks.toLocaleString()} link clicks`);
62
+ }
63
+ if (p.user_profile_clicks) {
64
+ metrics.push(account.platform === 'pinterest'
65
+ ? `${p.user_profile_clicks.toLocaleString()} pin clicks`
66
+ : `${p.user_profile_clicks.toLocaleString()} profile clicks`);
67
+ }
58
68
  lines.push(`• [${postedAt}] ${caption}`);
59
69
  lines.push(` ${metrics.join(' • ')}`);
60
70
  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.0",
3
+ "version": "0.8.1",
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": "./dist/index.js"
27
+ "posterly-mcp": "dist/index.js"
28
28
  },
29
29
  "main": "./dist/index.js",
30
30
  "scripts": {
@@ -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;
@@ -156,6 +157,8 @@ export interface PostAnalyticsRow {
156
157
  shares: number;
157
158
  plays: number;
158
159
  total_interactions: number;
160
+ url_link_clicks?: number;
161
+ user_profile_clicks?: number;
159
162
  media_type: string | null;
160
163
  media_url: string | null;
161
164
  permalink: string | null;
@@ -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, and Google Business Profile. Returns follower growth, reach, views, engagement rate, and platform-specific metrics (e.g. website clicks, direction requests for GBP).',
7
+ 'Get daily analytics snapshots and a period summary for a connected social account. Supports Instagram, LinkedIn, Google Business Profile, and Pinterest. 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,10 @@ 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
+ }
64
71
  }
65
72
 
66
73
  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, and Google Business Profile. Returns the most recent posts first.',
7
+ 'Get per-post engagement metrics (likes, comments, reach, impressions, saves, shares, plays, clicks) for a connected social account. Supports Instagram, LinkedIn, Google Business Profile, and Pinterest. Returns the most recent posts first.',
8
8
  inputSchema: z.object({
9
9
  account_id: z
10
10
  .number()
@@ -67,6 +67,20 @@ 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 (p.url_link_clicks) {
71
+ metrics.push(
72
+ account.platform === 'pinterest'
73
+ ? `${p.url_link_clicks.toLocaleString()} outbound clicks`
74
+ : `${p.url_link_clicks.toLocaleString()} link clicks`
75
+ );
76
+ }
77
+ if (p.user_profile_clicks) {
78
+ metrics.push(
79
+ account.platform === 'pinterest'
80
+ ? `${p.user_profile_clicks.toLocaleString()} pin clicks`
81
+ : `${p.user_profile_clicks.toLocaleString()} profile clicks`
82
+ );
83
+ }
70
84
 
71
85
  lines.push(`• [${postedAt}] ${caption}`);
72
86
  lines.push(` ${metrics.join(' • ')}`);
@@ -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(),