posterly-mcp-server 0.46.0 → 0.47.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.
@@ -29,6 +29,31 @@ const instagramSettingsSchema = z.object({
29
29
  reel_thumb_offset: z.number().nonnegative().optional(),
30
30
  is_ai_generated: z.boolean().optional().describe('Label the parent Instagram media container as AI-generated. For carousels, set on the parent only.'),
31
31
  audio_id: z.string().optional().describe('Licensed Instagram audio ID for Reels. Meta-linked Facebook Login accounts only.'),
32
+ caption_addon: z.union([
33
+ z.object({
34
+ type: z.enum(['none', 'poll', 'comment_prompt']).optional(),
35
+ kind: z.enum(['none', 'poll', 'comment_prompt']).optional(),
36
+ options: z.array(z.string()).max(4).optional(),
37
+ extended_duration: z.boolean().optional(),
38
+ poll_attachment: z.object({
39
+ option_a: z.string().optional(),
40
+ option_b: z.string().optional(),
41
+ option_c: z.string().optional(),
42
+ option_d: z.string().optional(),
43
+ options: z.array(z.string()).max(4).optional(),
44
+ }).optional(),
45
+ }),
46
+ z.null(),
47
+ ]).optional().describe('Optional Instagram caption add-on. type is none, poll, or comment_prompt. A poll needs 2 to 4 options of 1 to 25 characters (an emoji counts as one) and the caption is the question. A comment prompt uses the caption as the prompt. Facebook Login only. Not available on Stories. At most one add-on per post. Pass null or type none to clear it.'),
48
+ poll_attachment: z.object({
49
+ option_a: z.string().optional(),
50
+ option_b: z.string().optional(),
51
+ option_c: z.string().optional(),
52
+ option_d: z.string().optional(),
53
+ options: z.array(z.string()).max(4).optional(),
54
+ }).optional().describe('Instagram poll choices. option_a and option_b are required, option_c and option_d are optional, and option_d requires option_c. Mutually exclusive with a comment prompt. The caption is the question. Serialised JSON must be 200 characters or fewer.'),
55
+ is_comment_prompt_used: z.boolean().optional().describe('Set true to attach an Instagram comment prompt. The caption is the prompt. Mutually exclusive with a poll. Facebook Login only. Not available on Stories.'),
56
+ is_poll_duration_extended: z.boolean().optional().describe('Set true only together with a poll, to request the longer Instagram poll duration.'),
32
57
  });
33
58
  const platformSettingsSchema = z.object({
34
59
  __type: z.string().optional(),
@@ -104,11 +129,22 @@ const platformSettingsSchema = z.object({
104
129
  paid_partnership: z.boolean().optional(),
105
130
  is_ai_generated: z.boolean().optional(),
106
131
  audio_id: z.string().optional(),
132
+ caption_addon: z.union([
133
+ z.object({
134
+ type: z.enum(['none', 'poll', 'comment_prompt']).optional(),
135
+ options: z.array(z.string()).max(4).optional(),
136
+ extended_duration: z.boolean().optional(),
137
+ }),
138
+ z.null(),
139
+ ]).optional(),
140
+ poll_attachment: z.record(z.string(), z.unknown()).optional(),
141
+ is_comment_prompt_used: z.boolean().optional(),
142
+ is_poll_duration_extended: z.boolean().optional(),
107
143
  ai_generated: z.boolean().optional(),
108
144
  }).passthrough();
109
145
  export const updatePostTool = {
110
146
  name: 'update_post',
111
- description: 'Change a scheduled or draft post. DESTRUCTIVE WRITE. Edit (default): caption, media, schedule, post_type, or settings; overwrites the existing post; cannot edit published or currently publishing posts; if a client has already approved the post, a caption edit reopens caption approval, a media or thumbnail edit reopens asset approval, and a reschedule, account change or settings change reopens both. status (scheduled, paused, or draft, with scheduled_at when scheduling a draft): pause, resume, or move back to draft; send it on its own. release_id (with optional group_id): set or repair external release/group IDs; send it on its own.\n\n' +
147
+ description: 'Change a scheduled or draft post. DESTRUCTIVE WRITE. Edit (default): caption, media, schedule, post_type, or settings; overwrites the existing post; cannot edit published or currently publishing posts; if a client has already approved the post, a caption edit reopens caption approval, a media or thumbnail edit reopens asset approval, and a reschedule, account change or settings change reopens both. status (scheduled, paused, or draft, with scheduled_at when scheduling a draft): pause, resume, or move back to draft; send it on its own. release_id (with optional group_id): set or repair external release/group IDs; send it on its own. approval_action (request, approve, request_changes, or reject): change client approval. Only a workspace owner or admin may do this. The actor is the API key user, never a brand guest. request emails the brand reviewers. approve, request_changes, and reject are team-side decisions. request_changes needs approval_note. Optional approval_axis is asset or caption; omit it for both. Send approval_action on its own.\n\n' +
112
148
  'REQUIRED BEFORE CALLING: Show the user a side-by-side of the CURRENT post (caption, scheduled time, media, status) and the PROPOSED change, and get explicit confirmation ("yes update it", "apply those changes") before calling. Do not auto-edit posts based on a general instruction - each change needs its own confirmation.',
113
149
  inputSchema: z.object({
114
150
  post_id: z.number().describe('The post ID to update'),
@@ -146,6 +182,18 @@ export const updatePostTool = {
146
182
  .optional()
147
183
  .describe('Set an external release ID instead of editing the post. Send with post_id and optional group_id only.'),
148
184
  group_id: z.string().min(1).max(160).optional().describe('With release_id only: an external group/campaign ID.'),
185
+ approval_action: z
186
+ .enum(['request', 'approve', 'request_changes', 'reject'])
187
+ .optional()
188
+ .describe('Change client approval instead of editing the post. Owner or admin only. The actor is the API key user, never a brand guest. Send on its own.'),
189
+ approval_axis: z
190
+ .enum(['asset', 'caption'])
191
+ .optional()
192
+ .describe('With approval_action approve, request_changes, or reject: which axis to decide. Omit for both. Not allowed with request.'),
193
+ approval_note: z
194
+ .string()
195
+ .optional()
196
+ .describe('Note stored with the approval change. Required for request_changes.'),
149
197
  confirm: z.literal(true).describe('Must be true after explicit user confirmation of the proposed change.'),
150
198
  }),
151
199
  /** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
@@ -169,6 +217,9 @@ export const updatePostTool = {
169
217
  return updateStatus(client, args);
170
218
  if (mode === 'release_id')
171
219
  return updateReleaseId(client, args);
220
+ if (mode === 'approval') {
221
+ return updateApproval(client, args);
222
+ }
172
223
  return editPost(client, args);
173
224
  },
174
225
  };
@@ -209,6 +260,17 @@ async function updateStatus(client, input) {
209
260
  const transition = result.transition || {};
210
261
  return `Post #${input.post_id} status changed from ${transition.old_status || 'unknown'} to ${transition.new_status || input.status}.`;
211
262
  }
263
+ async function updateApproval(client, input) {
264
+ if (input.confirm !== true) {
265
+ throw new Error('Refusing to change client approval without confirm=true after explicit user confirmation. Only a workspace owner or admin can do this, and the actor is the API key user, never a brand guest.');
266
+ }
267
+ const result = await client.setPostApproval(input.post_id, {
268
+ action: input.approval_action,
269
+ axis: input.approval_axis,
270
+ note: input.approval_note,
271
+ });
272
+ return JSON.stringify(result, null, 2);
273
+ }
212
274
  async function updateReleaseId(client, input) {
213
275
  const result = await client.updatePostReleaseId(input.post_id, {
214
276
  release_id: input.release_id,