posterly-mcp-server 0.44.0 → 0.45.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.
Files changed (236) hide show
  1. package/README.md +310 -111
  2. package/dist/index.js +190 -894
  3. package/dist/lib/alias-transport.d.ts +84 -0
  4. package/dist/lib/alias-transport.js +193 -0
  5. package/dist/lib/api-client.d.ts +18 -0
  6. package/dist/lib/api-client.js +55 -0
  7. package/dist/lib/preview-token-core.d.ts +62 -0
  8. package/dist/lib/preview-token-core.js +131 -0
  9. package/dist/lib/preview-token.d.ts +23 -0
  10. package/dist/lib/preview-token.js +44 -0
  11. package/dist/lib/tool-registry.d.ts +246 -0
  12. package/dist/lib/tool-registry.js +394 -0
  13. package/dist/lib/tool-runtime.d.ts +68 -0
  14. package/dist/lib/tool-runtime.js +85 -0
  15. package/dist/lib/version.d.ts +1 -1
  16. package/dist/lib/version.js +1 -1
  17. package/dist/tools/audit-google-business-profile.js +1 -1
  18. package/dist/tools/connect-account.d.ts +4 -4
  19. package/dist/tools/connect-account.js +2 -2
  20. package/dist/tools/create-connect-session.d.ts +2 -2
  21. package/dist/tools/create-connect-session.js +2 -2
  22. package/dist/tools/create-post.d.ts +30 -7
  23. package/dist/tools/create-post.js +75 -9
  24. package/dist/tools/create-posts-batch.d.ts +4 -4
  25. package/dist/tools/create-posts-batch.js +14 -2
  26. package/dist/tools/create-webhook.d.ts +2 -2
  27. package/dist/tools/delete-post.d.ts +34 -10
  28. package/dist/tools/delete-post.js +52 -16
  29. package/dist/tools/generate-captions.d.ts +2 -2
  30. package/dist/tools/generate-image.d.ts +4 -4
  31. package/dist/tools/generate-image.js +2 -2
  32. package/dist/tools/generate-video.d.ts +6 -6
  33. package/dist/tools/generate-video.js +1 -1
  34. package/dist/tools/get-agent-signup-info.js +2 -2
  35. package/dist/tools/get-post.d.ts +16 -1
  36. package/dist/tools/get-post.js +35 -19
  37. package/dist/tools/list-accounts.d.ts +49 -3
  38. package/dist/tools/list-accounts.js +148 -33
  39. package/dist/tools/list-analytics.d.ts +111 -0
  40. package/dist/tools/list-analytics.js +393 -0
  41. package/dist/tools/list-brands.d.ts +38 -3
  42. package/dist/tools/list-brands.js +138 -20
  43. package/dist/tools/list-comments.d.ts +29 -9
  44. package/dist/tools/list-comments.js +75 -34
  45. package/dist/tools/list-conversations.d.ts +32 -9
  46. package/dist/tools/list-conversations.js +63 -27
  47. package/dist/tools/list-google-business-media.js +1 -1
  48. package/dist/tools/list-google-business-reviews.d.ts +32 -9
  49. package/dist/tools/list-google-business-reviews.js +77 -52
  50. package/dist/tools/list-jobs.d.ts +60 -0
  51. package/dist/tools/list-jobs.js +81 -0
  52. package/dist/tools/list-platforms.d.ts +62 -3
  53. package/dist/tools/list-platforms.js +118 -9
  54. package/dist/tools/list-posts.d.ts +2 -2
  55. package/dist/tools/manage-comment.d.ts +102 -0
  56. package/dist/tools/manage-comment.js +125 -0
  57. package/dist/tools/manage-conversation.d.ts +79 -0
  58. package/dist/tools/manage-conversation.js +75 -0
  59. package/dist/tools/manage-google-business-media.d.ts +103 -0
  60. package/dist/tools/manage-google-business-media.js +126 -0
  61. package/dist/tools/manage-google-business-review.d.ts +146 -0
  62. package/dist/tools/manage-google-business-review.js +115 -0
  63. package/dist/tools/manage-oauth-client.d.ts +121 -0
  64. package/dist/tools/manage-oauth-client.js +94 -0
  65. package/dist/tools/manage-webhook.d.ts +99 -0
  66. package/dist/tools/manage-webhook.js +87 -0
  67. package/dist/tools/start-signup.d.ts +2 -2
  68. package/dist/tools/submit-agent-feedback.d.ts +4 -4
  69. package/dist/tools/trigger-platform-helper.d.ts +2 -2
  70. package/dist/tools/update-post.d.ts +62 -11
  71. package/dist/tools/update-post.js +79 -26
  72. package/dist/tools/upload-media.d.ts +32 -9
  73. package/dist/tools/upload-media.js +54 -18
  74. package/dist/tools/validate-post.d.ts +4 -4
  75. package/dist/tools/validate-post.js +2 -2
  76. package/dist/tools/whoami.d.ts +15 -2
  77. package/dist/tools/whoami.js +221 -25
  78. package/package.json +5 -3
  79. package/scripts/test-alias-transport.mjs +368 -0
  80. package/scripts/test-client-header-sanitization.mjs +78 -0
  81. package/scripts/test-payload-builders.mjs +143 -20
  82. package/scripts/test-tool-annotations.mjs +38 -17
  83. package/server.json +9 -2
  84. package/src/index.ts +227 -1443
  85. package/src/lib/alias-transport.ts +233 -0
  86. package/src/lib/api-client.ts +65 -0
  87. package/src/lib/preview-token-core.ts +157 -0
  88. package/src/lib/preview-token.ts +54 -0
  89. package/src/lib/tool-registry.ts +538 -0
  90. package/src/lib/tool-runtime.ts +117 -0
  91. package/src/lib/version.ts +1 -1
  92. package/src/tools/audit-google-business-profile.ts +1 -1
  93. package/src/tools/connect-account.ts +2 -2
  94. package/src/tools/create-connect-session.ts +2 -2
  95. package/src/tools/create-post.ts +87 -12
  96. package/src/tools/create-posts-batch.ts +13 -2
  97. package/src/tools/delete-post.ts +60 -19
  98. package/src/tools/generate-image.ts +2 -2
  99. package/src/tools/generate-video.ts +1 -1
  100. package/src/tools/get-agent-signup-info.ts +2 -2
  101. package/src/tools/get-post.ts +35 -21
  102. package/src/tools/list-accounts.ts +187 -39
  103. package/src/tools/list-analytics.ts +444 -0
  104. package/src/tools/list-brands.ts +162 -26
  105. package/src/tools/list-comments.ts +86 -43
  106. package/src/tools/list-conversations.ts +81 -39
  107. package/src/tools/list-google-business-media.ts +1 -1
  108. package/src/tools/list-google-business-reviews.ts +93 -64
  109. package/src/tools/list-jobs.ts +91 -0
  110. package/src/tools/list-platforms.ts +135 -11
  111. package/src/tools/manage-comment.ts +142 -0
  112. package/src/tools/manage-conversation.ts +89 -0
  113. package/src/tools/manage-google-business-media.ts +147 -0
  114. package/src/tools/manage-google-business-review.ts +139 -0
  115. package/src/tools/manage-oauth-client.ts +116 -0
  116. package/src/tools/manage-webhook.ts +103 -0
  117. package/src/tools/update-post.ts +106 -39
  118. package/src/tools/upload-media.ts +76 -28
  119. package/src/tools/validate-post.ts +2 -1
  120. package/src/tools/whoami.ts +253 -30
  121. package/tool-annotations.json +76 -149
  122. package/tool-registry.json +2085 -0
  123. package/dist/tools/add-google-business-media.d.ts +0 -42
  124. package/dist/tools/add-google-business-media.js +0 -34
  125. package/dist/tools/create-oauth-client.d.ts +0 -40
  126. package/dist/tools/create-oauth-client.js +0 -20
  127. package/dist/tools/delete-comment.d.ts +0 -24
  128. package/dist/tools/delete-comment.js +0 -22
  129. package/dist/tools/delete-google-business-media.d.ts +0 -32
  130. package/dist/tools/delete-google-business-media.js +0 -17
  131. package/dist/tools/delete-google-business-review-reply.d.ts +0 -32
  132. package/dist/tools/delete-google-business-review-reply.js +0 -17
  133. package/dist/tools/delete-oauth-client.d.ts +0 -20
  134. package/dist/tools/delete-oauth-client.js +0 -13
  135. package/dist/tools/delete-post-group.d.ts +0 -20
  136. package/dist/tools/delete-post-group.js +0 -13
  137. package/dist/tools/delete-webhook.d.ts +0 -20
  138. package/dist/tools/delete-webhook.js +0 -13
  139. package/dist/tools/get-account-analytics.d.ts +0 -31
  140. package/dist/tools/get-account-analytics.js +0 -152
  141. package/dist/tools/get-brand-profile.d.ts +0 -16
  142. package/dist/tools/get-brand-profile.js +0 -73
  143. package/dist/tools/get-brand.d.ts +0 -16
  144. package/dist/tools/get-brand.js +0 -26
  145. package/dist/tools/get-comment.d.ts +0 -20
  146. package/dist/tools/get-comment.js +0 -36
  147. package/dist/tools/get-connect-link.d.ts +0 -28
  148. package/dist/tools/get-connect-link.js +0 -51
  149. package/dist/tools/get-connect-session.d.ts +0 -20
  150. package/dist/tools/get-connect-session.js +0 -54
  151. package/dist/tools/get-conversation.d.ts +0 -24
  152. package/dist/tools/get-conversation.js +0 -31
  153. package/dist/tools/get-google-business-review-link.d.ts +0 -24
  154. package/dist/tools/get-google-business-review-link.js +0 -19
  155. package/dist/tools/get-image-job.d.ts +0 -24
  156. package/dist/tools/get-image-job.js +0 -31
  157. package/dist/tools/get-learned-voice.d.ts +0 -16
  158. package/dist/tools/get-learned-voice.js +0 -22
  159. package/dist/tools/get-mcp-status.d.ts +0 -8
  160. package/dist/tools/get-mcp-status.js +0 -182
  161. package/dist/tools/get-performance-profile.d.ts +0 -16
  162. package/dist/tools/get-performance-profile.js +0 -39
  163. package/dist/tools/get-platform-schema.d.ts +0 -24
  164. package/dist/tools/get-platform-schema.js +0 -48
  165. package/dist/tools/get-post-analytics.d.ts +0 -39
  166. package/dist/tools/get-post-analytics.js +0 -222
  167. package/dist/tools/get-post-insights.d.ts +0 -28
  168. package/dist/tools/get-post-insights.js +0 -39
  169. package/dist/tools/get-post-missing.d.ts +0 -16
  170. package/dist/tools/get-post-missing.js +0 -12
  171. package/dist/tools/get-video-job.d.ts +0 -24
  172. package/dist/tools/get-video-job.js +0 -31
  173. package/dist/tools/list-brand-accounts.d.ts +0 -16
  174. package/dist/tools/list-brand-accounts.js +0 -24
  175. package/dist/tools/reply-google-business-review.d.ts +0 -36
  176. package/dist/tools/reply-google-business-review.js +0 -18
  177. package/dist/tools/reply-to-comment.d.ts +0 -32
  178. package/dist/tools/reply-to-comment.js +0 -17
  179. package/dist/tools/send-message.d.ts +0 -28
  180. package/dist/tools/send-message.js +0 -16
  181. package/dist/tools/suggest-google-business-review-reply.d.ts +0 -48
  182. package/dist/tools/suggest-google-business-review-reply.js +0 -27
  183. package/dist/tools/sync-inbox.d.ts +0 -24
  184. package/dist/tools/sync-inbox.js +0 -18
  185. package/dist/tools/test-webhook.d.ts +0 -20
  186. package/dist/tools/test-webhook.js +0 -14
  187. package/dist/tools/update-comment.d.ts +0 -28
  188. package/dist/tools/update-comment.js +0 -21
  189. package/dist/tools/update-oauth-client.d.ts +0 -44
  190. package/dist/tools/update-oauth-client.js +0 -21
  191. package/dist/tools/update-post-release-id.d.ts +0 -28
  192. package/dist/tools/update-post-release-id.js +0 -18
  193. package/dist/tools/update-post-status.d.ts +0 -28
  194. package/dist/tools/update-post-status.js +0 -23
  195. package/dist/tools/update-webhook.d.ts +0 -40
  196. package/dist/tools/update-webhook.js +0 -21
  197. package/dist/tools/upload-media-from-url.d.ts +0 -24
  198. package/dist/tools/upload-media-from-url.js +0 -26
  199. package/src/tools/add-google-business-media.ts +0 -50
  200. package/src/tools/create-oauth-client.ts +0 -36
  201. package/src/tools/delete-comment.ts +0 -29
  202. package/src/tools/delete-google-business-media.ts +0 -30
  203. package/src/tools/delete-google-business-review-reply.ts +0 -30
  204. package/src/tools/delete-oauth-client.ts +0 -23
  205. package/src/tools/delete-post-group.ts +0 -17
  206. package/src/tools/delete-webhook.ts +0 -17
  207. package/src/tools/get-account-analytics.ts +0 -171
  208. package/src/tools/get-brand-profile.ts +0 -86
  209. package/src/tools/get-brand.ts +0 -34
  210. package/src/tools/get-comment.ts +0 -47
  211. package/src/tools/get-connect-link.ts +0 -61
  212. package/src/tools/get-connect-session.ts +0 -63
  213. package/src/tools/get-conversation.ts +0 -44
  214. package/src/tools/get-google-business-review-link.ts +0 -30
  215. package/src/tools/get-image-job.ts +0 -36
  216. package/src/tools/get-learned-voice.ts +0 -31
  217. package/src/tools/get-mcp-status.ts +0 -212
  218. package/src/tools/get-performance-profile.ts +0 -47
  219. package/src/tools/get-platform-schema.ts +0 -56
  220. package/src/tools/get-post-analytics.ts +0 -243
  221. package/src/tools/get-post-insights.ts +0 -60
  222. package/src/tools/get-post-missing.ts +0 -16
  223. package/src/tools/get-video-job.ts +0 -36
  224. package/src/tools/list-brand-accounts.ts +0 -36
  225. package/src/tools/reply-google-business-review.ts +0 -32
  226. package/src/tools/reply-to-comment.ts +0 -30
  227. package/src/tools/send-message.ts +0 -28
  228. package/src/tools/suggest-google-business-review-reply.ts +0 -44
  229. package/src/tools/sync-inbox.ts +0 -28
  230. package/src/tools/test-webhook.ts +0 -18
  231. package/src/tools/update-comment.ts +0 -31
  232. package/src/tools/update-oauth-client.ts +0 -38
  233. package/src/tools/update-post-release-id.ts +0 -25
  234. package/src/tools/update-post-status.ts +0 -31
  235. package/src/tools/update-webhook.ts +0 -36
  236. package/src/tools/upload-media-from-url.ts +0 -33
@@ -0,0 +1,111 @@
1
+ import { z } from 'zod';
2
+ import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { type ToolContext } from '../lib/tool-runtime.js';
4
+ declare const presentationSchema: z.ZodOptional<z.ZodEnum<["compact", "table", "json"]>>;
5
+ type AnalyticsPresentation = z.infer<typeof presentationSchema>;
6
+ type Checkpoint = '1h' | '6h' | '24h' | '72h' | '7d';
7
+ type ListAnalyticsInput = {
8
+ view: 'accounts' | 'posts' | 'insights';
9
+ account_id?: number;
10
+ from?: string;
11
+ to?: string;
12
+ limit?: number;
13
+ offset?: number;
14
+ post_id?: number;
15
+ checkpoint?: Checkpoint;
16
+ presentation?: AnalyticsPresentation;
17
+ };
18
+ export declare const listAnalyticsTool: {
19
+ name: string;
20
+ description: string;
21
+ inputSchema: z.ZodObject<{
22
+ view: z.ZodEnum<["accounts", "posts", "insights"]>;
23
+ account_id: z.ZodOptional<z.ZodUnion<[z.ZodNumber, z.ZodString]>>;
24
+ from: z.ZodOptional<z.ZodString>;
25
+ to: z.ZodOptional<z.ZodString>;
26
+ limit: z.ZodOptional<z.ZodNumber>;
27
+ offset: z.ZodOptional<z.ZodNumber>;
28
+ post_id: z.ZodOptional<z.ZodNumber>;
29
+ checkpoint: z.ZodOptional<z.ZodEnum<["1h", "6h", "24h", "72h", "7d"]>>;
30
+ presentation: z.ZodOptional<z.ZodEnum<["compact", "table", "json"]>>;
31
+ }, "strip", z.ZodTypeAny, {
32
+ view: "accounts" | "posts" | "insights";
33
+ account_id?: string | number | undefined;
34
+ post_id?: number | undefined;
35
+ checkpoint?: "1h" | "6h" | "24h" | "72h" | "7d" | undefined;
36
+ limit?: number | undefined;
37
+ offset?: number | undefined;
38
+ from?: string | undefined;
39
+ to?: string | undefined;
40
+ presentation?: "compact" | "table" | "json" | undefined;
41
+ }, {
42
+ view: "accounts" | "posts" | "insights";
43
+ account_id?: string | number | undefined;
44
+ post_id?: number | undefined;
45
+ checkpoint?: "1h" | "6h" | "24h" | "72h" | "7d" | undefined;
46
+ limit?: number | undefined;
47
+ offset?: number | undefined;
48
+ from?: string | undefined;
49
+ to?: string | undefined;
50
+ presentation?: "compact" | "table" | "json" | undefined;
51
+ }>;
52
+ /** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
53
+ legacySchemas: {
54
+ get_account_analytics: z.ZodObject<{
55
+ account_id: z.ZodNumber;
56
+ from: z.ZodOptional<z.ZodString>;
57
+ to: z.ZodOptional<z.ZodString>;
58
+ presentation: z.ZodOptional<z.ZodEnum<["compact", "table", "json"]>>;
59
+ }, "strip", z.ZodTypeAny, {
60
+ account_id: number;
61
+ from?: string | undefined;
62
+ to?: string | undefined;
63
+ presentation?: "compact" | "table" | "json" | undefined;
64
+ }, {
65
+ account_id: number;
66
+ from?: string | undefined;
67
+ to?: string | undefined;
68
+ presentation?: "compact" | "table" | "json" | undefined;
69
+ }>;
70
+ get_post_analytics: z.ZodObject<{
71
+ account_id: z.ZodNumber;
72
+ from: z.ZodOptional<z.ZodString>;
73
+ to: z.ZodOptional<z.ZodString>;
74
+ limit: z.ZodOptional<z.ZodNumber>;
75
+ offset: z.ZodOptional<z.ZodNumber>;
76
+ presentation: z.ZodOptional<z.ZodEnum<["compact", "table", "json"]>>;
77
+ }, "strip", z.ZodTypeAny, {
78
+ account_id: number;
79
+ limit?: number | undefined;
80
+ offset?: number | undefined;
81
+ from?: string | undefined;
82
+ to?: string | undefined;
83
+ presentation?: "compact" | "table" | "json" | undefined;
84
+ }, {
85
+ account_id: number;
86
+ limit?: number | undefined;
87
+ offset?: number | undefined;
88
+ from?: string | undefined;
89
+ to?: string | undefined;
90
+ presentation?: "compact" | "table" | "json" | undefined;
91
+ }>;
92
+ get_post_insights: z.ZodObject<{
93
+ account_id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
94
+ post_id: z.ZodOptional<z.ZodNumber>;
95
+ checkpoint: z.ZodOptional<z.ZodEnum<["1h", "6h", "24h", "72h", "7d"]>>;
96
+ limit: z.ZodOptional<z.ZodNumber>;
97
+ }, "strip", z.ZodTypeAny, {
98
+ account_id?: string | number | undefined;
99
+ post_id?: number | undefined;
100
+ checkpoint?: "1h" | "6h" | "24h" | "72h" | "7d" | undefined;
101
+ limit?: number | undefined;
102
+ }, {
103
+ account_id?: string | number | undefined;
104
+ post_id?: number | undefined;
105
+ checkpoint?: "1h" | "6h" | "24h" | "72h" | "7d" | undefined;
106
+ limit?: number | undefined;
107
+ }>;
108
+ };
109
+ execute(client: PosterlyClient, input: ListAnalyticsInput, ctx: ToolContext): Promise<string>;
110
+ };
111
+ export {};
@@ -0,0 +1,393 @@
1
+ import { z } from 'zod';
2
+ import { formatDateTime, mdEmpty, mdKeyValue, mdSection, mdTitle, truncateText } from '../lib/format.js';
3
+ import { validateArgs } from '../lib/tool-runtime.js';
4
+ const presentationSchema = z.enum(['compact', 'table', 'json']).optional();
5
+ export const listAnalyticsTool = {
6
+ name: 'list_analytics',
7
+ description: 'Analytics for connected social accounts (Instagram, Facebook, LinkedIn, Google Business Profile, Pinterest, YouTube, and Threads). view=accounts (account_id): daily snapshots and a period summary with platform-native display_metrics labels. view=posts (account_id): per-post likes, comments/replies, reach, impressions/views, saves, shares, reposts, quotes, plays, clicks, and watch time, most recent first, with limit/offset. view=insights: per-post feedback insights with a performance tier (great/good/mixed/poor), diagnosis, next action, and baseline, filtered by account_id, post_id, or checkpoint (1h, 6h, 24h, 72h, 7d); read-only, Pro plan or higher. presentation (accounts and posts): compact for Telegram/mobile, table for Markdown clients, json for custom chart/card renderers.',
8
+ inputSchema: z.object({
9
+ view: z.enum(['accounts', 'posts', 'insights']).describe('accounts, posts, or insights.'),
10
+ account_id: z
11
+ .union([z.number(), z.string()])
12
+ .optional()
13
+ .describe('The social account ID (from list_accounts). Required for accounts and posts; optional filter for insights.'),
14
+ from: z
15
+ .string()
16
+ .optional()
17
+ .describe('accounts and posts: start date (ISO date, e.g. 2026-03-19). Defaults to 30 days ago.'),
18
+ to: z
19
+ .string()
20
+ .optional()
21
+ .describe('accounts and posts: end date (ISO date). Defaults to today.'),
22
+ limit: z
23
+ .number()
24
+ .min(1)
25
+ .max(200)
26
+ .optional()
27
+ .describe('posts: number of posts (default 50, max 200). insights: max insights (default 20, max 100).'),
28
+ offset: z.number().min(0).optional().describe('posts: pagination offset'),
29
+ post_id: z.number().int().positive().optional().describe('insights: filter to one post ID.'),
30
+ checkpoint: z
31
+ .enum(['1h', '6h', '24h', '72h', '7d'])
32
+ .optional()
33
+ .describe('insights: filter to one checkpoint after publish.'),
34
+ presentation: presentationSchema.describe('accounts and posts: compact bullets, Markdown table, or raw JSON for client-side chart/card rendering.'),
35
+ }),
36
+ /** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
37
+ legacySchemas: {
38
+ get_account_analytics: z.object({
39
+ account_id: z.number(),
40
+ from: z.string().optional(),
41
+ to: z.string().optional(),
42
+ presentation: presentationSchema,
43
+ }),
44
+ get_post_analytics: z.object({
45
+ account_id: z.number(),
46
+ from: z.string().optional(),
47
+ to: z.string().optional(),
48
+ limit: z.number().min(1).max(200).optional(),
49
+ offset: z.number().min(0).optional(),
50
+ presentation: presentationSchema,
51
+ }),
52
+ get_post_insights: z.object({
53
+ account_id: z.union([z.string(), z.number()]).optional(),
54
+ post_id: z.number().int().positive().optional(),
55
+ checkpoint: z.enum(['1h', '6h', '24h', '72h', '7d']).optional(),
56
+ limit: z.number().int().min(1).max(100).optional(),
57
+ }),
58
+ },
59
+ async execute(client, input, ctx) {
60
+ const { mode, args } = validateArgs(ctx, 'list_analytics', input);
61
+ if (mode === 'accounts') {
62
+ const result = await client.getAccountAnalytics(args);
63
+ return formatAccountAnalytics(result, args.presentation || 'compact');
64
+ }
65
+ if (mode === 'posts') {
66
+ const result = await client.getPostAnalytics(args);
67
+ return formatPostAnalytics(result, args.presentation || 'compact');
68
+ }
69
+ return postInsights(client, args);
70
+ },
71
+ };
72
+ async function postInsights(client, input) {
73
+ const { insights } = await client.getPostInsights(input);
74
+ if (!insights.length) {
75
+ return mdEmpty('feedback insights', 'No per-post feedback insights are available for these filters yet. Insights appear as posts hit their 1h, 6h, 24h, 72h, and 7d checkpoints.');
76
+ }
77
+ const blocks = insights.map((insight) => {
78
+ const heading = `${insight.title || 'Insight'} (${insight.performance_tier}, ${insight.checkpoint})`;
79
+ return mdSection(heading, mdKeyValue([
80
+ ['Post ID', insight.post_id],
81
+ ['Platform', insight.platform],
82
+ ['Account', insight.username ? `@${insight.username}` : String(insight.social_account_id)],
83
+ ['Diagnosis', truncateText(insight.diagnosis, 220)],
84
+ ['Next action', truncateText(insight.next_action, 220)],
85
+ ['Impact score', insight.impact_score],
86
+ ['Confidence', insight.confidence],
87
+ ['Published', formatDateTime(insight.published_at)],
88
+ ['Permalink', insight.permalink || undefined],
89
+ ]));
90
+ });
91
+ return [mdTitle(`Post feedback insights (${insights.length})`), ...blocks].join('\n\n');
92
+ }
93
+ function formatAccountAnalytics(result, presentation) {
94
+ const { account, range, summary, snapshots } = result;
95
+ const rows = accountMetricRows(summary, account.platform);
96
+ const insights = analyticsInsights(summary, account.platform);
97
+ if (presentation === 'json') {
98
+ return JSON.stringify({
99
+ type: 'account_analytics',
100
+ account,
101
+ range,
102
+ snapshot_count: snapshots.length,
103
+ metrics: rows.map(([label, value]) => ({ label, value })),
104
+ insights,
105
+ summary,
106
+ snapshots,
107
+ }, null, 2);
108
+ }
109
+ if (presentation === 'table') {
110
+ return [
111
+ `**📊 Analytics for ${accountLabel(account.username)} (${account.id})**`,
112
+ `_${account.platform} · ${range.from} → ${range.to}_`,
113
+ '',
114
+ '**Account**',
115
+ markdownTable(['Field', 'Value'], [
116
+ ['Account ID', String(account.id)],
117
+ ['Daily snapshots', formatNumber(snapshots.length)],
118
+ ]),
119
+ '',
120
+ '**Metrics**',
121
+ markdownTable(['Metric', 'Value'], rows),
122
+ '',
123
+ '**Quick read**',
124
+ insights.map((insight) => `- ${insight}`).join('\n'),
125
+ ].join('\n');
126
+ }
127
+ return [
128
+ `**📊 Analytics for ${accountLabel(account.username)} (${account.id})**`,
129
+ `_${account.platform} · ${range.from} → ${range.to}_`,
130
+ '',
131
+ '**Account**',
132
+ `- **Account ID:** ${account.id}`,
133
+ `- **Daily snapshots:** ${formatNumber(snapshots.length)}`,
134
+ '',
135
+ '**Key metrics**',
136
+ rows.map(([label, value]) => `- **${label}:** ${value}`).join('\n'),
137
+ '',
138
+ '**Quick read**',
139
+ insights.map((insight) => `- ${insight}`).join('\n'),
140
+ ].join('\n');
141
+ }
142
+ function accountMetricRows(summary, platform) {
143
+ const nativeRows = nativeMetricRows(summary);
144
+ if (nativeRows.length > 0)
145
+ return nativeRows;
146
+ if (platform === 'google_business') {
147
+ const metrics = summary.platform_metrics || {};
148
+ return [
149
+ ['Profile Views', formatNumber(metrics.profile_views ?? summary.total_views)],
150
+ ['Search Views', formatNumber(metrics.search_views ?? summary.total_reach)],
151
+ ['Maps Views', formatNumber(metrics.maps_views ?? summary.total_profile_views)],
152
+ ['Customer Actions', formatNumber(metrics.customer_actions ?? summary.total_accounts_engaged)],
153
+ ['Posts', formatNumber(metrics.posts ?? summary.current_media_count)],
154
+ ];
155
+ }
156
+ const rows = genericMetricRows(summary);
157
+ if (platform === 'pinterest')
158
+ rows.push(['Outbound clicks', formatNumber(summary.total_website_clicks)]);
159
+ if (platform === 'youtube') {
160
+ rows.push(['Watch minutes', formatNumber(summary.total_watch_minutes)]);
161
+ rows.push(['Likes', formatNumber(summary.total_likes)]);
162
+ }
163
+ return rows;
164
+ }
165
+ function nativeMetricRows(summary) {
166
+ return (summary.display_metrics || [])
167
+ .filter((metric) => metric?.label)
168
+ .map((metric) => [metric.label, formatNumber(metric.value)]);
169
+ }
170
+ function genericMetricRows(summary) {
171
+ return [
172
+ ['Followers', `${formatNumber(summary.current_followers)} (${formatDelta(summary.followers_change)} in range)`],
173
+ ['Follows gained / lost', `+${formatNumber(summary.total_follows_gained)} / -${formatNumber(summary.total_follows_lost)}`],
174
+ ['Total reach', formatNumber(summary.total_reach)],
175
+ ['Total views', formatNumber(summary.total_views)],
176
+ ['Profile views', formatNumber(summary.total_profile_views)],
177
+ ['Accounts engaged', formatNumber(summary.total_accounts_engaged)],
178
+ ['Engagement rate by reach', formatPercent(summary.engagement_rate)],
179
+ ['Engagement rate by followers', formatPercent(summary.engagement_rate_by_followers)],
180
+ ];
181
+ }
182
+ function analyticsInsights(summary, platform) {
183
+ const insights = [];
184
+ if (platform === 'google_business' && (summary.display_metrics?.length || 0) > 0) {
185
+ insights.push('Google Business metrics are shown with dashboard-native labels.');
186
+ }
187
+ if ((summary.followers_change || 0) > 0)
188
+ insights.push(`Audience grew by ${formatDelta(summary.followers_change)} followers in this range.`);
189
+ if ((summary.followers_change || 0) < 0)
190
+ insights.push(`Audience dipped by ${formatDelta(summary.followers_change)} followers in this range.`);
191
+ if (summary.engagement_rate != null && platform !== 'google_business')
192
+ insights.push(`Engagement by reach is ${formatPercent(summary.engagement_rate)}.`);
193
+ if (platform === 'youtube' && summary.total_watch_minutes != null)
194
+ insights.push(`${formatNumber(summary.total_watch_minutes)} total watch minutes were recorded.`);
195
+ return insights.length ? insights : ['No standout movement detected in the returned period.'];
196
+ }
197
+ function formatDelta(n) {
198
+ return n >= 0 ? `+${n.toLocaleString()}` : n.toLocaleString();
199
+ }
200
+ function formatNumber(value) {
201
+ return value == null ? 'n/a' : value.toLocaleString();
202
+ }
203
+ function formatPercent(value) {
204
+ return value == null ? 'n/a' : `${formatNumber(value)}%`;
205
+ }
206
+ function accountLabel(username) {
207
+ if (!username)
208
+ return 'account';
209
+ return username.startsWith('@') || /\s/.test(username) ? username : `@${username}`;
210
+ }
211
+ function markdownTable(headers, rows) {
212
+ return [
213
+ `| ${headers.map(escapeCell).join(' | ')} |`,
214
+ `| ${headers.map(() => '---').join(' | ')} |`,
215
+ ...rows.map((row) => `| ${row.map(escapeCell).join(' | ')} |`),
216
+ ].join('\n');
217
+ }
218
+ function escapeCell(value) {
219
+ return String(value).replace(/\|/g, '\\|').replace(/\r?\n/g, '<br>');
220
+ }
221
+ function formatPostAnalytics(result, presentation) {
222
+ const { account, range, posts, total } = result;
223
+ if (posts.length === 0) {
224
+ return `No analytics found for ${accountLabel(account.username)} (${account.platform}) between ${range.from} and ${range.to}.`;
225
+ }
226
+ const totals = posts.reduce((acc, post) => {
227
+ acc.likes += post.likes || 0;
228
+ acc.comments += post.comments || 0;
229
+ acc.reach += post.reach || 0;
230
+ acc.impressions += post.impressions || 0;
231
+ acc.shares += post.shares || 0;
232
+ acc.reposts += post.reposts || 0;
233
+ acc.quotes += post.quotes || 0;
234
+ acc.saves += post.saved || 0;
235
+ return acc;
236
+ }, { likes: 0, comments: 0, reach: 0, impressions: 0, shares: 0, reposts: 0, quotes: 0, saves: 0 });
237
+ const isThreads = account.platform === 'threads';
238
+ if (presentation === 'json') {
239
+ return JSON.stringify({
240
+ type: 'post_analytics',
241
+ account,
242
+ range,
243
+ returned: {
244
+ count: posts.length,
245
+ total,
246
+ },
247
+ totals,
248
+ posts,
249
+ }, null, 2);
250
+ }
251
+ if (presentation === 'table') {
252
+ if (isThreads) {
253
+ return [
254
+ `**📈 Post analytics for ${accountLabel(account.username)} (${account.id})**`,
255
+ `_Threads · ${range.from} → ${range.to} · showing ${posts.length} of ${total}_`,
256
+ '',
257
+ '**Returned totals**',
258
+ markdownTable(['Views', 'Likes', 'Replies', 'Reposts', 'Quotes', 'Shares'], [[
259
+ formatNumber(totals.impressions),
260
+ formatNumber(totals.likes),
261
+ formatNumber(totals.comments),
262
+ formatNumber(totals.reposts),
263
+ formatNumber(totals.quotes),
264
+ formatNumber(totals.shares),
265
+ ]]),
266
+ '',
267
+ '**Posts**',
268
+ markdownTable(['Posted', 'Post', 'Caption', 'Views', 'Likes', 'Replies', 'Reposts', 'Quotes', 'Shares'], posts.map((post) => [
269
+ dateTime(post.posted_at),
270
+ post.id ? String(post.id) : post.platform_media_id,
271
+ compact(post.caption_snippet, 70) || '(no caption)',
272
+ formatNumber(post.impressions),
273
+ formatNumber(post.likes),
274
+ formatNumber(post.comments),
275
+ formatNumber(post.reposts),
276
+ formatNumber(post.quotes),
277
+ formatNumber(post.shares),
278
+ ])),
279
+ ].join('\n');
280
+ }
281
+ return [
282
+ `**📈 Post analytics for ${accountLabel(account.username)} (${account.id})**`,
283
+ `_${account.platform} · ${range.from} → ${range.to} · showing ${posts.length} of ${total}_`,
284
+ '',
285
+ '**Returned totals**',
286
+ markdownTable(['Likes', 'Comments', 'Reach', 'Impressions', 'Shares', 'Saves'], [[
287
+ formatNumber(totals.likes),
288
+ formatNumber(totals.comments),
289
+ formatNumber(totals.reach),
290
+ formatNumber(totals.impressions),
291
+ formatNumber(totals.shares),
292
+ formatNumber(totals.saves),
293
+ ]]),
294
+ '',
295
+ '**Posts**',
296
+ markdownTable(['Posted', 'Post', 'Caption', 'Reach', 'Likes', 'Comments', 'Extras'], posts.map((post) => [
297
+ dateTime(post.posted_at),
298
+ post.id ? String(post.id) : post.platform_media_id,
299
+ compact(post.caption_snippet, 70) || '(no caption)',
300
+ formatNumber(post.reach),
301
+ formatNumber(post.likes),
302
+ formatNumber(post.comments),
303
+ postExtras(account.platform, post),
304
+ ])),
305
+ ].join('\n');
306
+ }
307
+ const lines = [
308
+ `**📈 Post analytics for ${accountLabel(account.username)} (${account.id})**`,
309
+ `_${account.platform} · ${range.from} → ${range.to} · showing ${posts.length} of ${total}_`,
310
+ '',
311
+ '**Returned totals**',
312
+ `- **Likes:** ${formatNumber(totals.likes)}`,
313
+ `- **Comments:** ${formatNumber(totals.comments)}`,
314
+ `- **Reach:** ${formatNumber(totals.reach)}`,
315
+ `- **Impressions:** ${formatNumber(totals.impressions)}`,
316
+ `- **Shares:** ${formatNumber(totals.shares)}`,
317
+ `- **Saves:** ${formatNumber(totals.saves)}`,
318
+ '',
319
+ '**Posts**',
320
+ ];
321
+ if (isThreads) {
322
+ const threadLines = [
323
+ `**📈 Post analytics for ${accountLabel(account.username)} (${account.id})**`,
324
+ `_Threads · ${range.from} → ${range.to} · showing ${posts.length} of ${total}_`,
325
+ '',
326
+ '**Returned totals**',
327
+ `- **Views:** ${formatNumber(totals.impressions)}`,
328
+ `- **Likes:** ${formatNumber(totals.likes)}`,
329
+ `- **Replies:** ${formatNumber(totals.comments)}`,
330
+ `- **Reposts:** ${formatNumber(totals.reposts)}`,
331
+ `- **Quotes:** ${formatNumber(totals.quotes)}`,
332
+ `- **Shares:** ${formatNumber(totals.shares)}`,
333
+ '',
334
+ '**Posts**',
335
+ ];
336
+ posts.forEach((post, index) => {
337
+ threadLines.push(`${index + 1}. **${compact(post.caption_snippet, 70) || '(no caption)'}**`);
338
+ threadLines.push(` Posted: ${dateTime(post.posted_at)}`);
339
+ threadLines.push(` Post ID: ${post.id ? String(post.id) : post.platform_media_id}`);
340
+ threadLines.push(` Views: ${formatNumber(post.impressions)}`);
341
+ threadLines.push(` Likes: ${formatNumber(post.likes)}`);
342
+ threadLines.push(` Replies: ${formatNumber(post.comments)}`);
343
+ threadLines.push(` Reposts: ${formatNumber(post.reposts)}`);
344
+ threadLines.push(` Quotes: ${formatNumber(post.quotes)}`);
345
+ threadLines.push(` Shares: ${formatNumber(post.shares)}`);
346
+ if (post.permalink)
347
+ threadLines.push(` Permalink: ${post.permalink}`);
348
+ });
349
+ return threadLines.join('\n');
350
+ }
351
+ posts.forEach((post, index) => {
352
+ lines.push(`${index + 1}. **${compact(post.caption_snippet, 70) || '(no caption)'}**`);
353
+ lines.push(` Posted: ${dateTime(post.posted_at)}`);
354
+ lines.push(` Post ID: ${post.id ? String(post.id) : post.platform_media_id}`);
355
+ lines.push(` Reach: ${formatNumber(post.reach)}`);
356
+ lines.push(` Likes: ${formatNumber(post.likes)}`);
357
+ lines.push(` Comments: ${formatNumber(post.comments)}`);
358
+ lines.push(` Extras: ${postExtras(account.platform, post)}`);
359
+ });
360
+ return lines.join('\n');
361
+ }
362
+ function postExtras(platform, post) {
363
+ const metrics = [];
364
+ if (post.impressions)
365
+ metrics.push(`${formatNumber(post.impressions)} impressions`);
366
+ if (post.saved)
367
+ metrics.push(`${formatNumber(post.saved)} saved`);
368
+ if (platform !== 'threads' && post.shares)
369
+ metrics.push(`${formatNumber(post.shares)} shares`);
370
+ if (post.plays)
371
+ metrics.push(`${formatNumber(post.plays)} plays`);
372
+ if (platform === 'youtube' && post.total_watch_time_ms)
373
+ metrics.push(`${formatNumber(Math.round(post.total_watch_time_ms / 60000))} watch minutes`);
374
+ if (platform === 'youtube' && post.avg_watch_time_ms)
375
+ metrics.push(`${formatNumber(Math.round(post.avg_watch_time_ms / 1000))}s avg view`);
376
+ if (post.url_link_clicks)
377
+ metrics.push(platform === 'pinterest' ? `${formatNumber(post.url_link_clicks)} outbound clicks` : `${formatNumber(post.url_link_clicks)} link clicks`);
378
+ if (post.user_profile_clicks)
379
+ metrics.push(platform === 'pinterest' ? `${formatNumber(post.user_profile_clicks)} pin clicks` : `${formatNumber(post.user_profile_clicks)} profile clicks`);
380
+ if (post.permalink)
381
+ metrics.push(post.permalink);
382
+ return metrics.join(' · ') || 'n/a';
383
+ }
384
+ function dateTime(value) {
385
+ if (!value)
386
+ return 'n/a';
387
+ const date = new Date(value);
388
+ return Number.isNaN(date.getTime()) ? value : date.toLocaleString();
389
+ }
390
+ function compact(value, maxLength) {
391
+ const text = String(value || '').replace(/\s+/g, ' ').trim();
392
+ return text.length > maxLength ? `${text.slice(0, Math.max(0, maxLength - 1))}…` : text;
393
+ }
@@ -1,16 +1,51 @@
1
1
  import { z } from 'zod';
2
2
  import type { PosterlyClient } from '../lib/api-client.js';
3
+ import { type ToolContext } from '../lib/tool-runtime.js';
4
+ type ListBrandsInput = {
5
+ view?: 'brands' | 'brand' | 'accounts' | 'profile';
6
+ brand_id?: string;
7
+ workspace_id?: string;
8
+ };
3
9
  export declare const listBrandsTool: {
4
10
  name: string;
5
11
  description: string;
6
12
  inputSchema: z.ZodObject<{
13
+ view: z.ZodOptional<z.ZodEnum<["brands", "brand", "accounts", "profile"]>>;
14
+ brand_id: z.ZodOptional<z.ZodString>;
7
15
  workspace_id: z.ZodOptional<z.ZodString>;
8
16
  }, "strip", z.ZodTypeAny, {
9
17
  workspace_id?: string | undefined;
18
+ brand_id?: string | undefined;
19
+ view?: "accounts" | "brands" | "brand" | "profile" | undefined;
10
20
  }, {
11
21
  workspace_id?: string | undefined;
22
+ brand_id?: string | undefined;
23
+ view?: "accounts" | "brands" | "brand" | "profile" | undefined;
12
24
  }>;
13
- execute(client: PosterlyClient, input: {
14
- workspace_id?: string;
15
- }): Promise<string>;
25
+ /** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
26
+ legacySchemas: {
27
+ get_brand: z.ZodObject<{
28
+ brand_id: z.ZodString;
29
+ }, "strip", z.ZodTypeAny, {
30
+ brand_id: string;
31
+ }, {
32
+ brand_id: string;
33
+ }>;
34
+ list_brand_accounts: z.ZodObject<{
35
+ brand_id: z.ZodString;
36
+ }, "strip", z.ZodTypeAny, {
37
+ brand_id: string;
38
+ }, {
39
+ brand_id: string;
40
+ }>;
41
+ get_brand_profile: z.ZodObject<{
42
+ brand_id: z.ZodString;
43
+ }, "strip", z.ZodTypeAny, {
44
+ brand_id: string;
45
+ }, {
46
+ brand_id: string;
47
+ }>;
48
+ };
49
+ execute(client: PosterlyClient, input: ListBrandsInput, ctx: ToolContext): Promise<string>;
16
50
  };
51
+ export {};