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
@@ -1,30 +1,148 @@
1
1
  import { z } from 'zod';
2
- import { code, mdEmpty, mdTable, mdTitle } from '../lib/format.js';
2
+ import { code, compactText, formatDateTime, mdEmpty, mdKeyValue, mdLearnedVoice, mdSection, mdTable, mdTitle } from '../lib/format.js';
3
+ import { validateArgs } from '../lib/tool-runtime.js';
3
4
  export const listBrandsTool = {
4
5
  name: 'list_brands',
5
- description: 'List brands/clients the caller can access. Returns each brand ID, name, workspace ID, source, and how many social accounts are currently assigned to it.',
6
+ description: 'Brands (clients) and their saved context. view=brands (default): brands the caller can access with brand ID, name, workspace ID, source, and how many social accounts are assigned (optional workspace_id). view=brand (brand_id): one brand with its workspace, source, and linked legacy brand group. view=accounts (brand_id): the connected social accounts assigned to the brand; use it when a user refers to a brand name rather than an account handle. view=profile (brand_id): voice and tone, audience, keywords, dos and don’ts, visual notes, and other saved brand context.',
6
7
  inputSchema: z.object({
8
+ view: z
9
+ .enum(['brands', 'brand', 'accounts', 'profile'])
10
+ .optional()
11
+ .describe('brands (default), brand, accounts, or profile.'),
12
+ brand_id: z.string().optional().describe('The brand ID (from view=brands). Required for brand, accounts, and profile.'),
7
13
  workspace_id: z
8
14
  .string()
9
15
  .optional()
10
- .describe('Filter to brands in a specific workspace (get IDs via whoami).'),
16
+ .describe('view=brands: filter to brands in a specific workspace (get IDs via whoami).'),
11
17
  }),
12
- async execute(client, input) {
13
- const brands = await client.listBrands({ workspace_id: input.workspace_id });
14
- if (brands.length === 0) {
15
- return input.workspace_id
16
- ? mdEmpty('brands', 'This workspace does not have any brands or clients yet.')
17
- : mdEmpty('brands', 'No brands or clients were found for this API key.', 'Create a brand in the posterly dashboard, then run this again.');
18
- }
19
- return [
20
- mdTitle(`Brands (${brands.length})`),
21
- mdTable(['Brand', 'Brand ID', 'Accounts', 'Source', 'Workspace'], brands.map((brand) => [
22
- brand.name,
23
- code(brand.id),
24
- brand.account_count,
25
- brand.source,
26
- brand.workspace_id ? code(brand.workspace_id) : 'default',
27
- ])),
28
- ].join('\n\n');
18
+ /** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
19
+ legacySchemas: {
20
+ get_brand: z.object({ brand_id: z.string() }),
21
+ list_brand_accounts: z.object({ brand_id: z.string() }),
22
+ get_brand_profile: z.object({ brand_id: z.string() }),
23
+ },
24
+ async execute(client, input, ctx) {
25
+ const { mode, args } = validateArgs(ctx, 'list_brands', input);
26
+ if (mode === 'brand')
27
+ return brand(client, args);
28
+ if (mode === 'accounts')
29
+ return brandAccounts(client, args);
30
+ if (mode === 'profile')
31
+ return brandProfile(client, args);
32
+ return brands(client, args);
29
33
  },
30
34
  };
35
+ async function brands(client, input) {
36
+ const brands = await client.listBrands({ workspace_id: input.workspace_id });
37
+ if (brands.length === 0) {
38
+ return input.workspace_id
39
+ ? mdEmpty('brands', 'This workspace does not have any brands or clients yet.')
40
+ : mdEmpty('brands', 'No brands or clients were found for this API key.', 'Create a brand in the posterly dashboard, then run this again.');
41
+ }
42
+ return [
43
+ mdTitle(`Brands (${brands.length})`),
44
+ mdTable(['Brand', 'Brand ID', 'Accounts', 'Source', 'Workspace'], brands.map((brand) => [
45
+ brand.name,
46
+ code(brand.id),
47
+ brand.account_count,
48
+ brand.source,
49
+ brand.workspace_id ? code(brand.workspace_id) : 'default',
50
+ ])),
51
+ ].join('\n\n');
52
+ }
53
+ async function brand(client, input) {
54
+ const result = await client.getBrand(input.brand_id);
55
+ const brand = result.brand;
56
+ return [
57
+ mdTitle(`Brand: ${brand.name}`),
58
+ mdKeyValue([
59
+ ['Brand ID', code(brand.id)],
60
+ ['Source', brand.source],
61
+ ['Workspace', brand.workspace_id ? code(brand.workspace_id) : 'n/a'],
62
+ ['Accounts assigned', brand.account_count ?? 0],
63
+ ['Workspace client ID', brand.workspace_client_id ? code(brand.workspace_client_id) : undefined],
64
+ ['Legacy brand group ID', brand.legacy_brand_group_id ? code(brand.legacy_brand_group_id) : undefined],
65
+ ['Created', formatDateTime(brand.created_at)],
66
+ ['Updated', formatDateTime(brand.updated_at)],
67
+ ]),
68
+ ].join('\n\n');
69
+ }
70
+ async function brandAccounts(client, input) {
71
+ const accounts = await client.listBrandAccounts(input.brand_id);
72
+ if (accounts.length === 0) {
73
+ return mdEmpty('brand accounts', 'No social accounts are currently assigned to this brand.');
74
+ }
75
+ return [
76
+ mdTitle(`Brand accounts (${accounts.length})`),
77
+ mdTable(['Platform', 'Account', 'Account ID', 'Workspace'], accounts.map((account) => [
78
+ account.platform,
79
+ `@${account.username}`,
80
+ code(account.id),
81
+ account.workspace_id ? code(account.workspace_id) : 'default',
82
+ ])),
83
+ ].join('\n\n');
84
+ }
85
+ async function brandProfile(client, input) {
86
+ const result = await client.getBrandProfile(input.brand_id);
87
+ const profile = result.brand_profile;
88
+ const profileBody = [
89
+ mdKeyValue([
90
+ ['Source', profile.source],
91
+ ['Profile ID', profile.id ? code(profile.id) : undefined],
92
+ ['Workspace client ID', profile.workspace_client_id ? code(profile.workspace_client_id) : undefined],
93
+ ['Legacy brand group ID', profile.brand_group_id ? code(profile.brand_group_id) : undefined],
94
+ ['Logo', profile.logo_url],
95
+ ['Last refreshed', profile.last_context_refresh_at ? formatDateTime(profile.last_context_refresh_at) : undefined],
96
+ ]),
97
+ mdSection('Voice and audience', mdKeyValue([
98
+ ['Tone of voice', compactText(profile.tone_of_voice, 180)],
99
+ ['Audience', compactText(profile.audience, 180)],
100
+ ['Brand values', compactText(profile.brand_values, 180)],
101
+ ['Brand story', compactText(formatStructuredValue(profile.brand_story), 240)],
102
+ ])),
103
+ mdSection('Guidance', mdKeyValue([
104
+ ['Custom instructions', compactText(profile.custom_instructions, 240)],
105
+ ['Keywords', formatStructuredValue(profile.keywords)],
106
+ ['Competitors', formatStructuredValue(profile.competitors)],
107
+ ['Do / Don’ts', formatStructuredValue(profile.do_donts)],
108
+ ['Topics to cover', formatStructuredValue(profile.topics_to_cover)],
109
+ ['Topics to avoid', formatStructuredValue(profile.topics_to_avoid)],
110
+ ])),
111
+ mdSection('Creative references', mdKeyValue([
112
+ ['Visual guidelines', formatStructuredValue(profile.visual_guidelines)],
113
+ ['Example posts', formatStructuredValue(profile.example_posts)],
114
+ ['Voice examples', formatStructuredValue(profile.voice_examples)],
115
+ ])),
116
+ ].filter(Boolean).join('\n\n');
117
+ const learnedVoices = (Array.isArray(profile.learned_voice) ? profile.learned_voice : []);
118
+ const learnedBody = learnedVoices.length
119
+ ? mdSection('Learned voice (from published captions)', learnedVoices.map((voice) => mdLearnedVoice(voice)).filter(Boolean).join('\n\n'))
120
+ : '';
121
+ return [
122
+ mdTitle(`Brand profile: ${profile.brand_name}`),
123
+ profileBody || 'No extended brand profile fields have been saved yet.',
124
+ learnedBody ||
125
+ '_No learned voice yet. posterly learns each account’s voice automatically once it has enough published posts._',
126
+ ].filter(Boolean).join('\n\n');
127
+ }
128
+ function formatStructuredValue(value) {
129
+ if (value == null)
130
+ return null;
131
+ if (typeof value === 'string') {
132
+ const trimmed = value.trim();
133
+ return trimmed.length > 0 ? trimmed : null;
134
+ }
135
+ if (Array.isArray(value)) {
136
+ if (value.length === 0)
137
+ return null;
138
+ return value
139
+ .map((item) => formatStructuredValue(item) || JSON.stringify(item))
140
+ .filter(Boolean)
141
+ .join(' | ');
142
+ }
143
+ if (typeof value === 'object') {
144
+ const json = JSON.stringify(value);
145
+ return json === '{}' ? null : json;
146
+ }
147
+ return String(value);
148
+ }
@@ -1,9 +1,21 @@
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 CommentsInput = {
5
+ comment_id?: string;
6
+ workspace_id?: string;
7
+ account_id?: number;
8
+ platform?: 'instagram' | 'facebook' | 'threads' | 'linkedin';
9
+ filter?: 'all' | 'unread' | 'flagged';
10
+ search?: string;
11
+ limit?: number;
12
+ offset?: number;
13
+ };
3
14
  export declare const listCommentsTool: {
4
15
  name: string;
5
16
  description: string;
6
17
  inputSchema: z.ZodObject<{
18
+ comment_id: z.ZodOptional<z.ZodString>;
7
19
  workspace_id: z.ZodOptional<z.ZodString>;
8
20
  account_id: z.ZodOptional<z.ZodNumber>;
9
21
  platform: z.ZodOptional<z.ZodEnum<["instagram", "facebook", "threads", "linkedin"]>>;
@@ -19,6 +31,7 @@ export declare const listCommentsTool: {
19
31
  offset?: number | undefined;
20
32
  filter?: "all" | "unread" | "flagged" | undefined;
21
33
  search?: string | undefined;
34
+ comment_id?: string | undefined;
22
35
  }, {
23
36
  platform?: "instagram" | "facebook" | "threads" | "linkedin" | undefined;
24
37
  workspace_id?: string | undefined;
@@ -27,14 +40,21 @@ export declare const listCommentsTool: {
27
40
  offset?: number | undefined;
28
41
  filter?: "all" | "unread" | "flagged" | undefined;
29
42
  search?: string | undefined;
43
+ comment_id?: string | undefined;
30
44
  }>;
31
- execute(client: PosterlyClient, input: {
32
- workspace_id?: string;
33
- account_id?: number;
34
- platform?: "instagram" | "facebook" | "threads" | "linkedin";
35
- filter?: "all" | "unread" | "flagged";
36
- search?: string;
37
- limit?: number;
38
- offset?: number;
39
- }): Promise<string>;
45
+ /** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
46
+ legacySchemas: {
47
+ get_comment: z.ZodObject<{
48
+ comment_id: z.ZodString;
49
+ workspace_id: z.ZodOptional<z.ZodString>;
50
+ }, "strip", z.ZodTypeAny, {
51
+ comment_id: string;
52
+ workspace_id?: string | undefined;
53
+ }, {
54
+ comment_id: string;
55
+ workspace_id?: string | undefined;
56
+ }>;
57
+ };
58
+ execute(client: PosterlyClient, input: CommentsInput, ctx: ToolContext): Promise<string>;
40
59
  };
60
+ export {};
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { truncateText } from '../lib/format.js';
2
+ import { formatLocalDateTime, truncateText } from '../lib/format.js';
3
+ import { validateArgs } from '../lib/tool-runtime.js';
3
4
  const INBOX_PLATFORMS = ['instagram', 'facebook', 'threads', 'linkedin'];
4
5
  /**
5
6
  * The shared `InboxConversationsResponse`/`InboxCommentsResponse` types
@@ -20,8 +21,9 @@ function describeSkippedWorkspaces(result) {
20
21
  }
21
22
  export const listCommentsTool = {
22
23
  name: 'list_comments',
23
- description: 'List social inbox comments and replies for Instagram, Facebook Pages, Threads, and LinkedIn Pages. Filter by account, platform, unread/flagged, or search. Requires a Pro plan or higher plus posts:read.',
24
+ description: 'Social inbox comments and replies for Instagram, Facebook Pages, Threads, and LinkedIn Pages (Pro plan or higher plus posts:read). Without comment_id: list comments filtered by account, platform, unread/flagged, or search, with limit/offset paging. With comment_id: one comment and its replies; only workspace_id may be added.',
24
25
  inputSchema: z.object({
26
+ comment_id: z.string().min(1).optional().describe('One comment (from a list call) with its replies.'),
25
27
  workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
26
28
  account_id: z.number().int().positive().optional().describe('Social account ID from list_accounts.'),
27
29
  platform: z.enum(INBOX_PLATFORMS).optional().describe('instagram, facebook, threads, or linkedin.'),
@@ -30,37 +32,76 @@ export const listCommentsTool = {
30
32
  limit: z.number().int().min(1).max(100).optional().describe('Number of comments to return. Default 50, max 100.'),
31
33
  offset: z.number().int().min(0).optional().describe('Skip this many comments before returning results.'),
32
34
  }),
33
- async execute(client, input) {
34
- const result = await client.listComments(input);
35
- const skippedNote = describeSkippedWorkspaces(result);
36
- if (result.comments.length === 0) {
37
- return [
38
- 'No inbox comments found matching your filters.',
39
- skippedNote,
40
- ].filter(Boolean).join('\n\n');
41
- }
42
- const lines = [`Inbox comments (showing ${result.comments.length} of ${result.total}):`];
43
- for (const comment of result.comments) {
44
- const author = typeof comment.author_username === 'string' ? `@${comment.author_username}` : 'unknown';
45
- const platform = typeof comment.platform === 'string' ? comment.platform : 'unknown';
46
- const flags = [];
47
- if (comment.is_read === false)
48
- flags.push('unread');
49
- if (comment.is_hidden)
50
- flags.push('hidden');
51
- if (comment.is_flagged)
52
- flags.push('flagged');
53
- const flagLabel = flags.length > 0 ? `, ${flags.join(', ')}` : '';
54
- const text = truncateText(typeof comment.content === 'string' ? comment.content : '', 80);
55
- const replies = Array.isArray(comment.replies) ? comment.replies.length : 0;
56
- lines.push(`- ${String(comment.id)} (${platform}, ${author}${flagLabel}${replies ? `, ${replies} replies` : ''}): ${text}`);
57
- }
58
- if (result.hasMore) {
59
- const nextOffset = (input.offset ?? 0) + result.comments.length;
60
- lines.push('', `More comments available. Call again with offset: ${nextOffset}.`);
61
- }
62
- if (skippedNote)
63
- lines.push('', skippedNote);
64
- return lines.join('\n');
35
+ /** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
36
+ legacySchemas: {
37
+ get_comment: z.object({
38
+ comment_id: z.string().min(1),
39
+ workspace_id: z.string().optional(),
40
+ }),
41
+ },
42
+ async execute(client, input, ctx) {
43
+ const { mode, args } = validateArgs(ctx, 'list_comments', input);
44
+ if (mode === 'comment')
45
+ return oneComment(client, args);
46
+ return commentList(client, args);
65
47
  },
66
48
  };
49
+ async function commentList(client, input) {
50
+ const result = await client.listComments(input);
51
+ const skippedNote = describeSkippedWorkspaces(result);
52
+ if (result.comments.length === 0) {
53
+ return [
54
+ 'No inbox comments found matching your filters.',
55
+ skippedNote,
56
+ ].filter(Boolean).join('\n\n');
57
+ }
58
+ const lines = [`Inbox comments (showing ${result.comments.length} of ${result.total}):`];
59
+ for (const comment of result.comments) {
60
+ const author = typeof comment.author_username === 'string' ? `@${comment.author_username}` : 'unknown';
61
+ const platform = typeof comment.platform === 'string' ? comment.platform : 'unknown';
62
+ const flags = [];
63
+ if (comment.is_read === false)
64
+ flags.push('unread');
65
+ if (comment.is_hidden)
66
+ flags.push('hidden');
67
+ if (comment.is_flagged)
68
+ flags.push('flagged');
69
+ const flagLabel = flags.length > 0 ? `, ${flags.join(', ')}` : '';
70
+ const text = truncateText(typeof comment.content === 'string' ? comment.content : '', 80);
71
+ const replies = Array.isArray(comment.replies) ? comment.replies.length : 0;
72
+ lines.push(`- ${String(comment.id)} (${platform}, ${author}${flagLabel}${replies ? `, ${replies} replies` : ''}): ${text}`);
73
+ }
74
+ if (result.hasMore) {
75
+ const nextOffset = (input.offset ?? 0) + result.comments.length;
76
+ lines.push('', `More comments available. Call again with offset: ${nextOffset}.`);
77
+ }
78
+ if (skippedNote)
79
+ lines.push('', skippedNote);
80
+ return lines.join('\n');
81
+ }
82
+ async function oneComment(client, input) {
83
+ const result = await client.getComment(input);
84
+ const comment = result.comment;
85
+ const author = typeof comment.author_username === 'string' ? `@${comment.author_username}` : 'unknown';
86
+ const lines = [
87
+ `Comment ${String(comment.id)}`,
88
+ `• Platform: ${typeof comment.platform === 'string' ? comment.platform : 'unknown'}`,
89
+ `• Author: ${author}`,
90
+ `• Hidden: ${comment.is_hidden ? 'yes' : 'no'}`,
91
+ `• Read: ${comment.is_read === false ? 'no' : 'yes'}`,
92
+ `• Posted: ${formatLocalDateTime(typeof comment.posted_at === 'string' ? comment.posted_at : null)}`,
93
+ `• Content: ${typeof comment.content === 'string' ? comment.content : '(empty)'}`,
94
+ ];
95
+ if (typeof comment.media_permalink === 'string')
96
+ lines.push(`• Media: ${comment.media_permalink}`);
97
+ const replies = Array.isArray(comment.replies) ? comment.replies : [];
98
+ if (replies.length > 0) {
99
+ lines.push('', `Replies (${replies.length}):`);
100
+ for (const reply of replies) {
101
+ const row = reply;
102
+ const replyAuthor = typeof row.author_username === 'string' ? `@${row.author_username}` : 'unknown';
103
+ lines.push(`- ${String(row.id)} ${replyAuthor}: ${typeof row.content === 'string' ? row.content : '(empty)'}`);
104
+ }
105
+ }
106
+ return lines.join('\n');
107
+ }
@@ -1,9 +1,21 @@
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 ConversationsInput = {
5
+ conversation_id?: string;
6
+ workspace_id?: string;
7
+ account_id?: number;
8
+ platform?: 'instagram' | 'facebook' | 'threads';
9
+ filter?: 'all' | 'unread' | 'starred' | 'archived';
10
+ search?: string;
11
+ limit?: number;
12
+ offset?: number;
13
+ };
3
14
  export declare const listConversationsTool: {
4
15
  name: string;
5
16
  description: string;
6
17
  inputSchema: z.ZodObject<{
18
+ conversation_id: z.ZodOptional<z.ZodString>;
7
19
  workspace_id: z.ZodOptional<z.ZodString>;
8
20
  account_id: z.ZodOptional<z.ZodNumber>;
9
21
  platform: z.ZodOptional<z.ZodEnum<["instagram", "facebook", "threads"]>>;
@@ -19,6 +31,7 @@ export declare const listConversationsTool: {
19
31
  offset?: number | undefined;
20
32
  filter?: "all" | "unread" | "starred" | "archived" | undefined;
21
33
  search?: string | undefined;
34
+ conversation_id?: string | undefined;
22
35
  }, {
23
36
  platform?: "instagram" | "facebook" | "threads" | undefined;
24
37
  workspace_id?: string | undefined;
@@ -27,14 +40,24 @@ export declare const listConversationsTool: {
27
40
  offset?: number | undefined;
28
41
  filter?: "all" | "unread" | "starred" | "archived" | undefined;
29
42
  search?: string | undefined;
43
+ conversation_id?: string | undefined;
30
44
  }>;
31
- execute(client: PosterlyClient, input: {
32
- workspace_id?: string;
33
- account_id?: number;
34
- platform?: "instagram" | "facebook" | "threads";
35
- filter?: "all" | "unread" | "starred" | "archived";
36
- search?: string;
37
- limit?: number;
38
- offset?: number;
39
- }): Promise<string>;
45
+ /** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
46
+ legacySchemas: {
47
+ get_conversation: z.ZodObject<{
48
+ conversation_id: z.ZodString;
49
+ workspace_id: z.ZodOptional<z.ZodString>;
50
+ limit: z.ZodOptional<z.ZodNumber>;
51
+ }, "strip", z.ZodTypeAny, {
52
+ conversation_id: string;
53
+ workspace_id?: string | undefined;
54
+ limit?: number | undefined;
55
+ }, {
56
+ conversation_id: string;
57
+ workspace_id?: string | undefined;
58
+ limit?: number | undefined;
59
+ }>;
60
+ };
61
+ execute(client: PosterlyClient, input: ConversationsInput, ctx: ToolContext): Promise<string>;
40
62
  };
63
+ export {};
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { truncateText } from '../lib/format.js';
2
+ import { formatLocalDateTime, truncateText } from '../lib/format.js';
3
+ import { validateArgs } from '../lib/tool-runtime.js';
3
4
  const INBOX_PLATFORMS = ['instagram', 'facebook', 'threads'];
4
5
  /**
5
6
  * The shared `InboxConversationsResponse`/`InboxCommentsResponse` types
@@ -20,39 +21,74 @@ function describeSkippedWorkspaces(result) {
20
21
  }
21
22
  export const listConversationsTool = {
22
23
  name: 'list_conversations',
23
- description: 'List social inbox DM conversations for Instagram and Facebook Page inboxes. Filter by account, platform, unread/starred/archived, or search. Requires a Pro plan or higher plus posts:read. Threads has no DMs.',
24
+ description: 'Social inbox DM conversations for Instagram and Facebook Page inboxes (Threads has no DMs; Pro plan or higher plus posts:read). Without conversation_id: list conversations filtered by account, platform, unread/starred/archived, or search, with limit/offset paging. With conversation_id: one conversation and its messages; there limit is the number of messages (default 50, max 100) and only workspace_id may be added.',
24
25
  inputSchema: z.object({
26
+ conversation_id: z.string().min(1).optional().describe('One conversation (from a list call) with its messages.'),
25
27
  workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
26
28
  account_id: z.number().int().positive().optional().describe('Social account ID from list_accounts.'),
27
29
  platform: z.enum(INBOX_PLATFORMS).optional().describe('instagram or facebook. Threads has no DMs.'),
28
30
  filter: z.enum(['all', 'unread', 'starred', 'archived']).optional().describe('Conversation filter. Defaults to all (excludes archived).'),
29
31
  search: z.string().optional().describe('Search participant username or last message preview.'),
30
- limit: z.number().int().min(1).max(100).optional().describe('Number of conversations to return. Default 50, max 100.'),
32
+ limit: z.number().int().min(1).max(100).optional().describe('Conversations to return, or messages with conversation_id. Default 50, max 100.'),
31
33
  offset: z.number().int().min(0).optional().describe('Skip this many conversations before returning results.'),
32
34
  }),
33
- async execute(client, input) {
34
- const result = await client.listConversations(input);
35
- const skippedNote = describeSkippedWorkspaces(result);
36
- if (result.conversations.length === 0) {
37
- return [
38
- 'No inbox conversations found matching your filters.',
39
- skippedNote,
40
- ].filter(Boolean).join('\n\n');
41
- }
42
- const lines = [`Inbox conversations (showing ${result.conversations.length} of ${result.total}):`];
43
- for (const conversation of result.conversations) {
44
- const username = typeof conversation.participant_username === 'string' ? `@${conversation.participant_username}` : 'unknown';
45
- const platform = typeof conversation.platform === 'string' ? conversation.platform : 'unknown';
46
- const unread = conversation.is_read === false ? 'unread' : 'read';
47
- const preview = truncateText(typeof conversation.last_message_preview === 'string' ? conversation.last_message_preview : '', 80);
48
- lines.push(`- ${String(conversation.id)} (${platform}, ${username}, ${unread}): ${preview}`);
49
- }
50
- if (result.hasMore) {
51
- const nextOffset = (input.offset ?? 0) + result.conversations.length;
52
- lines.push('', `More conversations available. Call again with offset: ${nextOffset}.`);
53
- }
54
- if (skippedNote)
55
- lines.push('', skippedNote);
56
- return lines.join('\n');
35
+ /** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
36
+ legacySchemas: {
37
+ get_conversation: z.object({
38
+ conversation_id: z.string().min(1),
39
+ workspace_id: z.string().optional(),
40
+ limit: z.number().int().min(1).max(100).optional(),
41
+ }),
42
+ },
43
+ async execute(client, input, ctx) {
44
+ const { mode, args } = validateArgs(ctx, 'list_conversations', input);
45
+ if (mode === 'conversation')
46
+ return oneConversation(client, args);
47
+ return conversationList(client, args);
57
48
  },
58
49
  };
50
+ async function conversationList(client, input) {
51
+ const result = await client.listConversations(input);
52
+ const skippedNote = describeSkippedWorkspaces(result);
53
+ if (result.conversations.length === 0) {
54
+ return [
55
+ 'No inbox conversations found matching your filters.',
56
+ skippedNote,
57
+ ].filter(Boolean).join('\n\n');
58
+ }
59
+ const lines = [`Inbox conversations (showing ${result.conversations.length} of ${result.total}):`];
60
+ for (const conversation of result.conversations) {
61
+ const username = typeof conversation.participant_username === 'string' ? `@${conversation.participant_username}` : 'unknown';
62
+ const platform = typeof conversation.platform === 'string' ? conversation.platform : 'unknown';
63
+ const unread = conversation.is_read === false ? 'unread' : 'read';
64
+ const preview = truncateText(typeof conversation.last_message_preview === 'string' ? conversation.last_message_preview : '', 80);
65
+ lines.push(`- ${String(conversation.id)} (${platform}, ${username}, ${unread}): ${preview}`);
66
+ }
67
+ if (result.hasMore) {
68
+ const nextOffset = (input.offset ?? 0) + result.conversations.length;
69
+ lines.push('', `More conversations available. Call again with offset: ${nextOffset}.`);
70
+ }
71
+ if (skippedNote)
72
+ lines.push('', skippedNote);
73
+ return lines.join('\n');
74
+ }
75
+ async function oneConversation(client, input) {
76
+ const result = await client.getConversation(input);
77
+ const conversation = result.conversation;
78
+ const username = typeof conversation.participant_username === 'string' ? `@${conversation.participant_username}` : 'unknown';
79
+ const lines = [
80
+ `Conversation ${String(conversation.id)}`,
81
+ `• Platform: ${typeof conversation.platform === 'string' ? conversation.platform : 'unknown'}`,
82
+ `• Participant: ${username}`,
83
+ `• Read: ${conversation.is_read === false ? 'no' : 'yes'}`,
84
+ `• Messages: ${result.messages.length}`,
85
+ '',
86
+ ];
87
+ for (const message of result.messages) {
88
+ const who = message.sender_is_business ? 'you' : (typeof message.sender_username === 'string' ? message.sender_username : 'them');
89
+ const when = formatLocalDateTime(typeof message.sent_at === 'string' ? message.sent_at : null);
90
+ const text = truncateText(typeof message.content === 'string' ? message.content : '', 240);
91
+ lines.push(`- [${when}] ${who}: ${text}`);
92
+ }
93
+ return lines.join('\n');
94
+ }
@@ -5,7 +5,7 @@ export const listGoogleBusinessMediaTool = {
5
5
  inputSchema: z.object({
6
6
  workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
7
7
  account_id: z.number().int().positive().optional().describe('Google Business account ID from list_accounts.'),
8
- location_id: z.string().optional().describe('Google Business numeric location id (the location_id/platform_user_id from list_accounts, e.g. "197940849675145390"). NOT the ChIJ... Place ID returned by get_google_business_review_link. Prefer account_id.'),
8
+ location_id: z.string().optional().describe('Google Business numeric location id (the location_id/platform_user_id from list_accounts, e.g. "197940849675145390"). NOT the ChIJ... Place ID from list_google_business_reviews view=review_link. Prefer account_id.'),
9
9
  }),
10
10
  async execute(client, input) {
11
11
  const result = await client.listGoogleBusinessMedia(input);
@@ -1,9 +1,21 @@
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 ReviewsInput = {
5
+ view?: 'reviews' | 'review_link';
6
+ workspace_id?: string;
7
+ account_id?: number;
8
+ location_id?: string;
9
+ rating?: number;
10
+ unanswered?: boolean;
11
+ limit?: number;
12
+ offset?: number;
13
+ };
3
14
  export declare const listGoogleBusinessReviewsTool: {
4
15
  name: string;
5
16
  description: string;
6
17
  inputSchema: z.ZodObject<{
18
+ view: z.ZodOptional<z.ZodEnum<["reviews", "review_link"]>>;
7
19
  workspace_id: z.ZodOptional<z.ZodString>;
8
20
  account_id: z.ZodOptional<z.ZodNumber>;
9
21
  location_id: z.ZodOptional<z.ZodString>;
@@ -19,6 +31,7 @@ export declare const listGoogleBusinessReviewsTool: {
19
31
  location_id?: string | undefined;
20
32
  rating?: number | undefined;
21
33
  unanswered?: boolean | undefined;
34
+ view?: "reviews" | "review_link" | undefined;
22
35
  }, {
23
36
  workspace_id?: string | undefined;
24
37
  account_id?: number | undefined;
@@ -27,14 +40,24 @@ export declare const listGoogleBusinessReviewsTool: {
27
40
  location_id?: string | undefined;
28
41
  rating?: number | undefined;
29
42
  unanswered?: boolean | undefined;
43
+ view?: "reviews" | "review_link" | undefined;
30
44
  }>;
31
- execute(client: PosterlyClient, input: {
32
- workspace_id?: string;
33
- account_id?: number;
34
- location_id?: string;
35
- rating?: number;
36
- unanswered?: boolean;
37
- limit?: number;
38
- offset?: number;
39
- }): Promise<string>;
45
+ /** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
46
+ legacySchemas: {
47
+ get_google_business_review_link: z.ZodObject<{
48
+ workspace_id: z.ZodOptional<z.ZodString>;
49
+ account_id: z.ZodOptional<z.ZodNumber>;
50
+ location_id: z.ZodOptional<z.ZodString>;
51
+ }, "strip", z.ZodTypeAny, {
52
+ workspace_id?: string | undefined;
53
+ account_id?: number | undefined;
54
+ location_id?: string | undefined;
55
+ }, {
56
+ workspace_id?: string | undefined;
57
+ account_id?: number | undefined;
58
+ location_id?: string | undefined;
59
+ }>;
60
+ };
61
+ execute(client: PosterlyClient, input: ReviewsInput, ctx: ToolContext): Promise<string>;
40
62
  };
63
+ export {};