posterly-mcp-server 0.46.0 → 0.47.0
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 +4 -3
- package/dist/lib/api-client.d.ts +11 -1
- package/dist/lib/api-client.js +12 -2
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/tools/create-post.d.ts +6 -6
- package/dist/tools/create-posts-batch.d.ts +4 -4
- package/dist/tools/delete-post.d.ts +4 -0
- package/dist/tools/delete-post.js +10 -5
- package/dist/tools/list-analytics.js +4 -1
- package/dist/tools/list-posts.d.ts +4 -0
- package/dist/tools/list-posts.js +5 -1
- package/dist/tools/update-post.d.ts +14 -2
- package/dist/tools/update-post.js +27 -1
- package/dist/tools/validate-post.d.ts +4 -4
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/lib/api-client.ts +24 -2
- package/src/lib/version.ts +1 -1
- package/src/tools/delete-post.ts +16 -7
- package/src/tools/list-analytics.ts +4 -1
- package/src/tools/list-posts.ts +6 -1
- package/src/tools/update-post.ts +46 -1
- package/tool-registry.json +40 -0
package/README.md
CHANGED
|
@@ -123,7 +123,7 @@ Add the same server definition to your Cursor MCP settings:
|
|
|
123
123
|
|
|
124
124
|
## Available tools
|
|
125
125
|
|
|
126
|
-
`posterly-mcp-server@0.
|
|
126
|
+
`posterly-mcp-server@0.47.0` exposes 62 tools covering 95 actions. The hosted HTTP endpoint exposes the same catalog minus the three signup tools: 59 tools covering 92 actions.
|
|
127
127
|
|
|
128
128
|
Many tools pick what to do with one parameter: `view` (reads), `action` (writes), or `type`. A few pick their mode from which parameters you pass (for example `update_post` with `status` changes the status instead of editing). Every tool description names all of its views and actions.
|
|
129
129
|
|
|
@@ -211,13 +211,14 @@ Accounts, posting, media, schedule, analytics, and support. What most assistants
|
|
|
211
211
|
| `edit` (default) | Edit caption, media, schedule, or settings |
|
|
212
212
|
| `status` (pass `status`) | Pause, resume, schedule, or move to draft |
|
|
213
213
|
| `release_id` (pass `release_id`) | Set a release or group ID |
|
|
214
|
+
| `approval` (pass `approval_action`) | Request, approve, request changes, or reject client review (owner or admin only) |
|
|
214
215
|
|
|
215
216
|
**`delete_post`**: Delete posts
|
|
216
217
|
|
|
217
218
|
| Mode (picked by params) | What it does |
|
|
218
219
|
| --- | --- |
|
|
219
|
-
| `post` (default) | Delete one post |
|
|
220
|
-
| `group` (pass `group_id`) | Delete a post group (preview then confirm) |
|
|
220
|
+
| `post` (default) | Delete one post. Published Instagram or Facebook posts also need `confirm_published` |
|
|
221
|
+
| `group` (pass `group_id`) | Delete a post group (preview then confirm). Never deletes published posts |
|
|
221
222
|
|
|
222
223
|
**`upload_media`**: Upload media
|
|
223
224
|
|
package/dist/lib/api-client.d.ts
CHANGED
|
@@ -145,6 +145,7 @@ export interface ApiPostApproval {
|
|
|
145
145
|
asset_status: ApiPostApprovalStatus;
|
|
146
146
|
caption_status: ApiPostApprovalStatus;
|
|
147
147
|
waiting_on: 'client' | 'team' | null;
|
|
148
|
+
pending_client_approval: boolean;
|
|
148
149
|
requested_at: string | null;
|
|
149
150
|
completed_at: string | null;
|
|
150
151
|
}
|
|
@@ -1212,6 +1213,7 @@ export declare class PosterlyClient {
|
|
|
1212
1213
|
offset?: number;
|
|
1213
1214
|
workspace_id?: string;
|
|
1214
1215
|
approval_status?: string;
|
|
1216
|
+
approval_waiting_on?: string;
|
|
1215
1217
|
brand_id?: string;
|
|
1216
1218
|
}): Promise<{
|
|
1217
1219
|
posts: Post[];
|
|
@@ -1244,10 +1246,18 @@ export declare class PosterlyClient {
|
|
|
1244
1246
|
}): Promise<{
|
|
1245
1247
|
post: Post;
|
|
1246
1248
|
}>;
|
|
1247
|
-
deletePost(id: number
|
|
1249
|
+
deletePost(id: number, options?: {
|
|
1250
|
+
confirmPublished?: boolean;
|
|
1251
|
+
}): Promise<{
|
|
1248
1252
|
deleted: boolean;
|
|
1249
1253
|
id: number;
|
|
1254
|
+
platform_delete?: string;
|
|
1250
1255
|
}>;
|
|
1256
|
+
setPostApproval(id: number, data: {
|
|
1257
|
+
action: 'request' | 'approve' | 'request_changes' | 'reject';
|
|
1258
|
+
axis?: 'asset' | 'caption';
|
|
1259
|
+
note?: string;
|
|
1260
|
+
}): Promise<Record<string, unknown>>;
|
|
1251
1261
|
updatePostStatus(id: number, data: {
|
|
1252
1262
|
status: 'scheduled' | 'paused' | 'draft';
|
|
1253
1263
|
scheduled_at?: string;
|
package/dist/lib/api-client.js
CHANGED
|
@@ -340,6 +340,8 @@ export class PosterlyClient {
|
|
|
340
340
|
searchParams.set('workspace_id', params.workspace_id);
|
|
341
341
|
if (params?.approval_status)
|
|
342
342
|
searchParams.set('approval_status', params.approval_status);
|
|
343
|
+
if (params?.approval_waiting_on)
|
|
344
|
+
searchParams.set('approval_waiting_on', params.approval_waiting_on);
|
|
343
345
|
if (params?.brand_id)
|
|
344
346
|
searchParams.set('brand_id', params.brand_id);
|
|
345
347
|
const qs = searchParams.toString();
|
|
@@ -375,8 +377,16 @@ export class PosterlyClient {
|
|
|
375
377
|
async updatePost(id, data) {
|
|
376
378
|
return this.request('PATCH', `/posts/${id}`, data);
|
|
377
379
|
}
|
|
378
|
-
async deletePost(id) {
|
|
379
|
-
|
|
380
|
+
async deletePost(id, options) {
|
|
381
|
+
const qs = options?.confirmPublished ? '?confirm_published=true' : '';
|
|
382
|
+
return this.request('DELETE', `/posts/${id}${qs}`);
|
|
383
|
+
}
|
|
384
|
+
async setPostApproval(id, data) {
|
|
385
|
+
return this.request('POST', `/posts/${id}/approval`, {
|
|
386
|
+
action: data.action,
|
|
387
|
+
...(data.axis ? { axis: data.axis } : {}),
|
|
388
|
+
...(data.note ? { note: data.note } : {}),
|
|
389
|
+
});
|
|
380
390
|
}
|
|
381
391
|
async updatePostStatus(id, data) {
|
|
382
392
|
return this.request('PUT', `/posts/${id}/status`, data);
|
package/dist/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const POSTERLY_MCP_VERSION = "0.
|
|
1
|
+
export declare const POSTERLY_MCP_VERSION = "0.47.0";
|
package/dist/lib/version.js
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
// tool set (minus the intentional pre-auth signup tools that only this stdio
|
|
6
6
|
// package exposes). `npm run check:mcp-parity` enforces both the version match
|
|
7
7
|
// and the tool-list match, and runs in the pre-commit hook.
|
|
8
|
-
export const POSTERLY_MCP_VERSION = '0.
|
|
8
|
+
export const POSTERLY_MCP_VERSION = '0.47.0';
|
|
@@ -972,11 +972,11 @@ export declare const createPostPayloadInputSchema: z.ZodObject<{
|
|
|
972
972
|
username?: string | undefined;
|
|
973
973
|
workspace_id?: string | undefined;
|
|
974
974
|
account_id?: string | undefined;
|
|
975
|
+
caption?: string | undefined;
|
|
975
976
|
scheduled_at?: string | undefined;
|
|
976
977
|
media_url?: string | undefined;
|
|
977
978
|
media_urls?: string[] | undefined;
|
|
978
979
|
post_type?: string | undefined;
|
|
979
|
-
caption?: string | undefined;
|
|
980
980
|
thread_posts?: string[] | undefined;
|
|
981
981
|
instagram_settings?: {
|
|
982
982
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -1135,11 +1135,11 @@ export declare const createPostPayloadInputSchema: z.ZodObject<{
|
|
|
1135
1135
|
username?: string | undefined;
|
|
1136
1136
|
workspace_id?: string | undefined;
|
|
1137
1137
|
account_id?: string | undefined;
|
|
1138
|
+
caption?: string | undefined;
|
|
1138
1139
|
scheduled_at?: string | undefined;
|
|
1139
1140
|
media_url?: string | undefined;
|
|
1140
1141
|
media_urls?: string[] | undefined;
|
|
1141
1142
|
post_type?: string | undefined;
|
|
1142
|
-
caption?: string | undefined;
|
|
1143
1143
|
thread_posts?: string[] | undefined;
|
|
1144
1144
|
instagram_settings?: {
|
|
1145
1145
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -1791,11 +1791,11 @@ export declare const createPostInputSchema: z.ZodObject<{
|
|
|
1791
1791
|
username?: string | undefined;
|
|
1792
1792
|
workspace_id?: string | undefined;
|
|
1793
1793
|
account_id?: string | undefined;
|
|
1794
|
+
caption?: string | undefined;
|
|
1794
1795
|
scheduled_at?: string | undefined;
|
|
1795
1796
|
media_url?: string | undefined;
|
|
1796
1797
|
media_urls?: string[] | undefined;
|
|
1797
1798
|
post_type?: string | undefined;
|
|
1798
|
-
caption?: string | undefined;
|
|
1799
1799
|
thread_posts?: string[] | undefined;
|
|
1800
1800
|
instagram_settings?: {
|
|
1801
1801
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -1955,11 +1955,11 @@ export declare const createPostInputSchema: z.ZodObject<{
|
|
|
1955
1955
|
username?: string | undefined;
|
|
1956
1956
|
workspace_id?: string | undefined;
|
|
1957
1957
|
account_id?: string | undefined;
|
|
1958
|
+
caption?: string | undefined;
|
|
1958
1959
|
scheduled_at?: string | undefined;
|
|
1959
1960
|
media_url?: string | undefined;
|
|
1960
1961
|
media_urls?: string[] | undefined;
|
|
1961
1962
|
post_type?: string | undefined;
|
|
1962
|
-
caption?: string | undefined;
|
|
1963
1963
|
thread_posts?: string[] | undefined;
|
|
1964
1964
|
instagram_settings?: {
|
|
1965
1965
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -2640,11 +2640,11 @@ export declare const createPostTool: {
|
|
|
2640
2640
|
username?: string | undefined;
|
|
2641
2641
|
workspace_id?: string | undefined;
|
|
2642
2642
|
account_id?: string | undefined;
|
|
2643
|
+
caption?: string | undefined;
|
|
2643
2644
|
scheduled_at?: string | undefined;
|
|
2644
2645
|
media_url?: string | undefined;
|
|
2645
2646
|
media_urls?: string[] | undefined;
|
|
2646
2647
|
post_type?: string | undefined;
|
|
2647
|
-
caption?: string | undefined;
|
|
2648
2648
|
thread_posts?: string[] | undefined;
|
|
2649
2649
|
instagram_settings?: {
|
|
2650
2650
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -2804,11 +2804,11 @@ export declare const createPostTool: {
|
|
|
2804
2804
|
username?: string | undefined;
|
|
2805
2805
|
workspace_id?: string | undefined;
|
|
2806
2806
|
account_id?: string | undefined;
|
|
2807
|
+
caption?: string | undefined;
|
|
2807
2808
|
scheduled_at?: string | undefined;
|
|
2808
2809
|
media_url?: string | undefined;
|
|
2809
2810
|
media_urls?: string[] | undefined;
|
|
2810
2811
|
post_type?: string | undefined;
|
|
2811
|
-
caption?: string | undefined;
|
|
2812
2812
|
thread_posts?: string[] | undefined;
|
|
2813
2813
|
instagram_settings?: {
|
|
2814
2814
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -499,11 +499,11 @@ export declare const createPostsBatchTool: {
|
|
|
499
499
|
username?: string | undefined;
|
|
500
500
|
workspace_id?: string | undefined;
|
|
501
501
|
account_id?: string | undefined;
|
|
502
|
+
caption?: string | undefined;
|
|
502
503
|
scheduled_at?: string | undefined;
|
|
503
504
|
media_url?: string | undefined;
|
|
504
505
|
media_urls?: string[] | undefined;
|
|
505
506
|
post_type?: string | undefined;
|
|
506
|
-
caption?: string | undefined;
|
|
507
507
|
thread_posts?: string[] | undefined;
|
|
508
508
|
instagram_settings?: {
|
|
509
509
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -662,11 +662,11 @@ export declare const createPostsBatchTool: {
|
|
|
662
662
|
username?: string | undefined;
|
|
663
663
|
workspace_id?: string | undefined;
|
|
664
664
|
account_id?: string | undefined;
|
|
665
|
+
caption?: string | undefined;
|
|
665
666
|
scheduled_at?: string | undefined;
|
|
666
667
|
media_url?: string | undefined;
|
|
667
668
|
media_urls?: string[] | undefined;
|
|
668
669
|
post_type?: string | undefined;
|
|
669
|
-
caption?: string | undefined;
|
|
670
670
|
thread_posts?: string[] | undefined;
|
|
671
671
|
instagram_settings?: {
|
|
672
672
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -829,11 +829,11 @@ export declare const createPostsBatchTool: {
|
|
|
829
829
|
username?: string | undefined;
|
|
830
830
|
workspace_id?: string | undefined;
|
|
831
831
|
account_id?: string | undefined;
|
|
832
|
+
caption?: string | undefined;
|
|
832
833
|
scheduled_at?: string | undefined;
|
|
833
834
|
media_url?: string | undefined;
|
|
834
835
|
media_urls?: string[] | undefined;
|
|
835
836
|
post_type?: string | undefined;
|
|
836
|
-
caption?: string | undefined;
|
|
837
837
|
thread_posts?: string[] | undefined;
|
|
838
838
|
instagram_settings?: {
|
|
839
839
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -995,11 +995,11 @@ export declare const createPostsBatchTool: {
|
|
|
995
995
|
username?: string | undefined;
|
|
996
996
|
workspace_id?: string | undefined;
|
|
997
997
|
account_id?: string | undefined;
|
|
998
|
+
caption?: string | undefined;
|
|
998
999
|
scheduled_at?: string | undefined;
|
|
999
1000
|
media_url?: string | undefined;
|
|
1000
1001
|
media_urls?: string[] | undefined;
|
|
1001
1002
|
post_type?: string | undefined;
|
|
1002
|
-
caption?: string | undefined;
|
|
1003
1003
|
thread_posts?: string[] | undefined;
|
|
1004
1004
|
instagram_settings?: {
|
|
1005
1005
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -4,6 +4,7 @@ import { type ToolContext } from '../lib/tool-runtime.js';
|
|
|
4
4
|
type DeletePostInput = {
|
|
5
5
|
post_id?: number;
|
|
6
6
|
group_id?: string;
|
|
7
|
+
confirm_published?: boolean;
|
|
7
8
|
confirm?: boolean;
|
|
8
9
|
preview_id?: string;
|
|
9
10
|
};
|
|
@@ -13,6 +14,7 @@ export declare const deletePostTool: {
|
|
|
13
14
|
inputSchema: z.ZodObject<{
|
|
14
15
|
post_id: z.ZodOptional<z.ZodNumber>;
|
|
15
16
|
group_id: z.ZodOptional<z.ZodString>;
|
|
17
|
+
confirm_published: z.ZodOptional<z.ZodBoolean>;
|
|
16
18
|
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
17
19
|
preview_id: z.ZodOptional<z.ZodString>;
|
|
18
20
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -20,11 +22,13 @@ export declare const deletePostTool: {
|
|
|
20
22
|
preview_id?: string | undefined;
|
|
21
23
|
post_id?: number | undefined;
|
|
22
24
|
group_id?: string | undefined;
|
|
25
|
+
confirm_published?: boolean | undefined;
|
|
23
26
|
}, {
|
|
24
27
|
confirm?: boolean | undefined;
|
|
25
28
|
preview_id?: string | undefined;
|
|
26
29
|
post_id?: number | undefined;
|
|
27
30
|
group_id?: string | undefined;
|
|
31
|
+
confirm_published?: boolean | undefined;
|
|
28
32
|
}>;
|
|
29
33
|
/** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
|
|
30
34
|
legacySchemas: {
|
|
@@ -3,10 +3,14 @@ import { code, dashboardUrlForPosts } from '../lib/format.js';
|
|
|
3
3
|
import { previewThenConfirm, validateArgs } from '../lib/tool-runtime.js';
|
|
4
4
|
export const deletePostTool = {
|
|
5
5
|
name: 'delete_post',
|
|
6
|
-
description: 'Delete posts. DESTRUCTIVE and IRREVERSIBLE: deleted posts and captions cannot be recovered
|
|
6
|
+
description: 'Delete posts. DESTRUCTIVE and IRREVERSIBLE: deleted posts and captions cannot be recovered. post_id: delete one post; fetch it with `get_post` first, show the user what will be deleted (caption, account, scheduled time), and pass confirm=true only after explicit confirmation ("yes delete it", "remove it"). A post that is currently publishing is never deleted. A published post is taken down only when the user also explicitly confirms the live removal and you pass confirm_published=true. Instagram and Facebook are attempted when the stored id and token allow it. Every other platform, or a live URL with no id, returns platform_delete_unsupported and the posterly row is kept. A published post with no live URL and no platform id is removed from posterly only. group_id: delete every draft, scheduled, failed, or paused post matching a caller-defined group_id, post_group_id, api_group_id, or release_id. A group delete never removes published posts. Preview then confirm: call without confirm to get a preview and preview_id, show it, then call again with confirm=true and that preview_id. Never delete several posts one by one without listing each and confirming the full list.',
|
|
7
7
|
inputSchema: z.object({
|
|
8
8
|
post_id: z.number().optional().describe('Delete this one post.'),
|
|
9
|
-
group_id: z.string().min(1).max(160).optional().describe('Delete every unpublished post in this group or release instead.'),
|
|
9
|
+
group_id: z.string().min(1).max(160).optional().describe('Delete every unpublished post in this group or release instead. Never deletes published posts.'),
|
|
10
|
+
confirm_published: z
|
|
11
|
+
.boolean()
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('With post_id only. True only after the user explicitly confirms taking a live Instagram or Facebook post down. Without it, a published post is refused and kept.'),
|
|
10
14
|
confirm: z
|
|
11
15
|
.boolean()
|
|
12
16
|
.optional()
|
|
@@ -33,7 +37,7 @@ export const deletePostTool = {
|
|
|
33
37
|
// GET /api/v1/posts has no group filter, so the preview says in plain
|
|
34
38
|
// words which posts match instead of listing them.
|
|
35
39
|
describe: async () => [
|
|
36
|
-
`Deletes every draft, scheduled, failed, or paused post whose group_id, post_group_id, api_group_id, or release_id is ${code(args.group_id)}.
|
|
40
|
+
`Deletes every draft, scheduled, failed, or paused post whose group_id, post_group_id, api_group_id, or release_id is ${code(args.group_id)}. A group delete never removes published posts. This cannot be undone.`,
|
|
37
41
|
'To see the posts first, call list_posts and check each post\'s group or release ID.',
|
|
38
42
|
].join('\n\n'),
|
|
39
43
|
run: async () => {
|
|
@@ -49,9 +53,10 @@ async function deleteOne(client, input) {
|
|
|
49
53
|
if (input.confirm !== true) {
|
|
50
54
|
throw new Error('Refusing to delete post without confirm=true after explicit user confirmation.');
|
|
51
55
|
}
|
|
52
|
-
const result = await client.deletePost(input.post_id);
|
|
56
|
+
const result = await client.deletePost(input.post_id, { confirmPublished: input.confirm_published === true });
|
|
57
|
+
const platformDelete = result.platform_delete ? ` Platform delete: ${result.platform_delete}.` : '';
|
|
53
58
|
return [
|
|
54
|
-
`Post #${result.id} deleted successfully
|
|
59
|
+
`Post #${result.id} deleted successfully.${platformDelete}`,
|
|
55
60
|
'It has been removed from the posterly queue.',
|
|
56
61
|
`View queue in posterly: ${dashboardUrlForPosts()}`,
|
|
57
62
|
'',
|
|
@@ -4,7 +4,7 @@ import { validateArgs } from '../lib/tool-runtime.js';
|
|
|
4
4
|
const presentationSchema = z.enum(['compact', 'table', 'json']).optional();
|
|
5
5
|
export const listAnalyticsTool = {
|
|
6
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.',
|
|
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. Google Business Profile account analytics includes Calls, Website clicks, and Directions along with Profile Views, Search Views, Maps Views, Customer Actions, and Posts. 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
8
|
inputSchema: z.object({
|
|
9
9
|
view: z.enum(['accounts', 'posts', 'insights']).describe('accounts, posts, or insights.'),
|
|
10
10
|
account_id: z
|
|
@@ -150,6 +150,9 @@ function accountMetricRows(summary, platform) {
|
|
|
150
150
|
['Search Views', formatNumber(metrics.search_views ?? summary.total_reach)],
|
|
151
151
|
['Maps Views', formatNumber(metrics.maps_views ?? summary.total_profile_views)],
|
|
152
152
|
['Customer Actions', formatNumber(metrics.customer_actions ?? summary.total_accounts_engaged)],
|
|
153
|
+
['Calls', formatNumber(metrics.call_clicks ?? summary.total_call_clicks)],
|
|
154
|
+
['Directions', formatNumber(metrics.direction_requests ?? summary.total_direction_requests)],
|
|
155
|
+
['Website clicks', formatNumber(metrics.website_clicks ?? summary.total_website_clicks)],
|
|
153
156
|
['Posts', formatNumber(metrics.posts ?? summary.current_media_count)],
|
|
154
157
|
];
|
|
155
158
|
}
|
|
@@ -10,6 +10,7 @@ export declare const listPostsTool: {
|
|
|
10
10
|
limit: z.ZodOptional<z.ZodNumber>;
|
|
11
11
|
workspace_id: z.ZodOptional<z.ZodString>;
|
|
12
12
|
approval_status: z.ZodOptional<z.ZodEnum<["none", "pending_client", "approved", "changes_requested", "rejected"]>>;
|
|
13
|
+
approval_waiting_on: z.ZodOptional<z.ZodEnum<["client", "team"]>>;
|
|
13
14
|
brand_id: z.ZodOptional<z.ZodString>;
|
|
14
15
|
}, "strip", z.ZodTypeAny, {
|
|
15
16
|
platform?: "instagram" | "instagram-standalone" | "facebook" | "tiktok" | "twitter" | "threads" | "linkedin" | "youtube" | "pinterest" | "google_business" | "x" | "telegram" | "bluesky" | "discord" | "wordpress" | "devto" | "hashnode" | "lemmy" | "slack" | "mastodon" | "gmb" | "google-business" | "google_business_profile" | undefined;
|
|
@@ -18,6 +19,7 @@ export declare const listPostsTool: {
|
|
|
18
19
|
account_id?: string | undefined;
|
|
19
20
|
limit?: number | undefined;
|
|
20
21
|
approval_status?: "none" | "pending_client" | "approved" | "changes_requested" | "rejected" | undefined;
|
|
22
|
+
approval_waiting_on?: "client" | "team" | undefined;
|
|
21
23
|
brand_id?: string | undefined;
|
|
22
24
|
}, {
|
|
23
25
|
platform?: "instagram" | "instagram-standalone" | "facebook" | "tiktok" | "twitter" | "threads" | "linkedin" | "youtube" | "pinterest" | "google_business" | "x" | "telegram" | "bluesky" | "discord" | "wordpress" | "devto" | "hashnode" | "lemmy" | "slack" | "mastodon" | "gmb" | "google-business" | "google_business_profile" | undefined;
|
|
@@ -26,6 +28,7 @@ export declare const listPostsTool: {
|
|
|
26
28
|
account_id?: string | undefined;
|
|
27
29
|
limit?: number | undefined;
|
|
28
30
|
approval_status?: "none" | "pending_client" | "approved" | "changes_requested" | "rejected" | undefined;
|
|
31
|
+
approval_waiting_on?: "client" | "team" | undefined;
|
|
29
32
|
brand_id?: string | undefined;
|
|
30
33
|
}>;
|
|
31
34
|
execute(client: PosterlyClient, input: {
|
|
@@ -35,6 +38,7 @@ export declare const listPostsTool: {
|
|
|
35
38
|
limit?: number;
|
|
36
39
|
workspace_id?: string;
|
|
37
40
|
approval_status?: string;
|
|
41
|
+
approval_waiting_on?: "client" | "team";
|
|
38
42
|
brand_id?: string;
|
|
39
43
|
}): Promise<string>;
|
|
40
44
|
};
|
package/dist/tools/list-posts.js
CHANGED
|
@@ -3,7 +3,7 @@ import { dashboardUrlForPostList, formatLocalDateTime, truncateText } from '../l
|
|
|
3
3
|
import { SUPPORTED_PLATFORM_INPUTS, SUPPORTED_PLATFORM_IDS } from '../generated/platform-manifest.js';
|
|
4
4
|
export const listPostsTool = {
|
|
5
5
|
name: 'list_posts',
|
|
6
|
-
description: 'List upcoming or recent posts. Filter by status (scheduled, published, failed, draft), platform, account_id, workspace_id, approval_status, or brand_id. Returns posts in your workspaces, teammates\' too, per your role there (brand-locked seats only see their assigned brands). If workspace_id is omitted, this searches every workspace you belong to.',
|
|
6
|
+
description: 'List upcoming or recent posts. Filter by status (scheduled, published, failed, draft), platform, account_id, workspace_id, approval_status, approval_waiting_on, or brand_id. approval_waiting_on=client is the same queue as approval_status=pending_client. Each post includes approval.pending_client_approval. Returns posts in your workspaces, teammates\' too, per your role there (brand-locked seats only see their assigned brands). If workspace_id is omitted, this searches every workspace you belong to.',
|
|
7
7
|
inputSchema: z.object({
|
|
8
8
|
status: z
|
|
9
9
|
.string()
|
|
@@ -31,6 +31,10 @@ export const listPostsTool = {
|
|
|
31
31
|
.enum(['none', 'pending_client', 'approved', 'changes_requested', 'rejected'])
|
|
32
32
|
.optional()
|
|
33
33
|
.describe('Filter by client review approval status. pending_client means the post is waiting on the client.'),
|
|
34
|
+
approval_waiting_on: z
|
|
35
|
+
.enum(['client', 'team'])
|
|
36
|
+
.optional()
|
|
37
|
+
.describe('client: approval_status pending_client. team: changes_requested or rejected. Conflicts with a non-overlapping approval_status.'),
|
|
34
38
|
brand_id: z
|
|
35
39
|
.string()
|
|
36
40
|
.optional()
|
|
@@ -497,6 +497,9 @@ type UpdatePostInput = EditInput & {
|
|
|
497
497
|
status?: 'scheduled' | 'paused' | 'draft';
|
|
498
498
|
release_id?: string;
|
|
499
499
|
group_id?: string;
|
|
500
|
+
approval_action?: 'request' | 'approve' | 'request_changes' | 'reject';
|
|
501
|
+
approval_axis?: 'asset' | 'caption';
|
|
502
|
+
approval_note?: string;
|
|
500
503
|
};
|
|
501
504
|
export declare const updatePostTool: {
|
|
502
505
|
name: string;
|
|
@@ -995,11 +998,15 @@ export declare const updatePostTool: {
|
|
|
995
998
|
status: z.ZodOptional<z.ZodEnum<["scheduled", "paused", "draft"]>>;
|
|
996
999
|
release_id: z.ZodOptional<z.ZodString>;
|
|
997
1000
|
group_id: z.ZodOptional<z.ZodString>;
|
|
1001
|
+
approval_action: z.ZodOptional<z.ZodEnum<["request", "approve", "request_changes", "reject"]>>;
|
|
1002
|
+
approval_axis: z.ZodOptional<z.ZodEnum<["asset", "caption"]>>;
|
|
1003
|
+
approval_note: z.ZodOptional<z.ZodString>;
|
|
998
1004
|
confirm: z.ZodLiteral<true>;
|
|
999
1005
|
}, "strip", z.ZodTypeAny, {
|
|
1000
1006
|
confirm: true;
|
|
1001
1007
|
post_id: number;
|
|
1002
1008
|
status?: "scheduled" | "paused" | "draft" | undefined;
|
|
1009
|
+
caption?: string | undefined;
|
|
1003
1010
|
scheduled_at?: string | undefined;
|
|
1004
1011
|
media_url?: string | undefined;
|
|
1005
1012
|
media_urls?: string[] | undefined;
|
|
@@ -1007,7 +1014,6 @@ export declare const updatePostTool: {
|
|
|
1007
1014
|
reel_cover_url?: string | undefined;
|
|
1008
1015
|
reel_cover_method?: string | undefined;
|
|
1009
1016
|
reel_thumb_offset?: number | undefined;
|
|
1010
|
-
caption?: string | undefined;
|
|
1011
1017
|
instagram_settings?: {
|
|
1012
1018
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
1013
1019
|
reel_cover_url?: string | undefined;
|
|
@@ -1163,10 +1169,14 @@ export declare const updatePostTool: {
|
|
|
1163
1169
|
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
1164
1170
|
release_id?: string | undefined;
|
|
1165
1171
|
group_id?: string | undefined;
|
|
1172
|
+
approval_action?: "request" | "approve" | "request_changes" | "reject" | undefined;
|
|
1173
|
+
approval_axis?: "asset" | "caption" | undefined;
|
|
1174
|
+
approval_note?: string | undefined;
|
|
1166
1175
|
}, {
|
|
1167
1176
|
confirm: true;
|
|
1168
1177
|
post_id: number;
|
|
1169
1178
|
status?: "scheduled" | "paused" | "draft" | undefined;
|
|
1179
|
+
caption?: string | undefined;
|
|
1170
1180
|
scheduled_at?: string | undefined;
|
|
1171
1181
|
media_url?: string | undefined;
|
|
1172
1182
|
media_urls?: string[] | undefined;
|
|
@@ -1174,7 +1184,6 @@ export declare const updatePostTool: {
|
|
|
1174
1184
|
reel_cover_url?: string | undefined;
|
|
1175
1185
|
reel_cover_method?: string | undefined;
|
|
1176
1186
|
reel_thumb_offset?: number | undefined;
|
|
1177
|
-
caption?: string | undefined;
|
|
1178
1187
|
instagram_settings?: {
|
|
1179
1188
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
1180
1189
|
reel_cover_url?: string | undefined;
|
|
@@ -1330,6 +1339,9 @@ export declare const updatePostTool: {
|
|
|
1330
1339
|
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
1331
1340
|
release_id?: string | undefined;
|
|
1332
1341
|
group_id?: string | undefined;
|
|
1342
|
+
approval_action?: "request" | "approve" | "request_changes" | "reject" | undefined;
|
|
1343
|
+
approval_axis?: "asset" | "caption" | undefined;
|
|
1344
|
+
approval_note?: string | undefined;
|
|
1333
1345
|
}>;
|
|
1334
1346
|
/** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
|
|
1335
1347
|
legacySchemas: {
|
|
@@ -108,7 +108,7 @@ const platformSettingsSchema = z.object({
|
|
|
108
108
|
}).passthrough();
|
|
109
109
|
export const updatePostTool = {
|
|
110
110
|
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' +
|
|
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. 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
112
|
'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
113
|
inputSchema: z.object({
|
|
114
114
|
post_id: z.number().describe('The post ID to update'),
|
|
@@ -146,6 +146,18 @@ export const updatePostTool = {
|
|
|
146
146
|
.optional()
|
|
147
147
|
.describe('Set an external release ID instead of editing the post. Send with post_id and optional group_id only.'),
|
|
148
148
|
group_id: z.string().min(1).max(160).optional().describe('With release_id only: an external group/campaign ID.'),
|
|
149
|
+
approval_action: z
|
|
150
|
+
.enum(['request', 'approve', 'request_changes', 'reject'])
|
|
151
|
+
.optional()
|
|
152
|
+
.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.'),
|
|
153
|
+
approval_axis: z
|
|
154
|
+
.enum(['asset', 'caption'])
|
|
155
|
+
.optional()
|
|
156
|
+
.describe('With approval_action approve, request_changes, or reject: which axis to decide. Omit for both. Not allowed with request.'),
|
|
157
|
+
approval_note: z
|
|
158
|
+
.string()
|
|
159
|
+
.optional()
|
|
160
|
+
.describe('Note stored with the approval change. Required for request_changes.'),
|
|
149
161
|
confirm: z.literal(true).describe('Must be true after explicit user confirmation of the proposed change.'),
|
|
150
162
|
}),
|
|
151
163
|
/** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
|
|
@@ -169,6 +181,9 @@ export const updatePostTool = {
|
|
|
169
181
|
return updateStatus(client, args);
|
|
170
182
|
if (mode === 'release_id')
|
|
171
183
|
return updateReleaseId(client, args);
|
|
184
|
+
if (mode === 'approval') {
|
|
185
|
+
return updateApproval(client, args);
|
|
186
|
+
}
|
|
172
187
|
return editPost(client, args);
|
|
173
188
|
},
|
|
174
189
|
};
|
|
@@ -209,6 +224,17 @@ async function updateStatus(client, input) {
|
|
|
209
224
|
const transition = result.transition || {};
|
|
210
225
|
return `Post #${input.post_id} status changed from ${transition.old_status || 'unknown'} to ${transition.new_status || input.status}.`;
|
|
211
226
|
}
|
|
227
|
+
async function updateApproval(client, input) {
|
|
228
|
+
if (input.confirm !== true) {
|
|
229
|
+
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.');
|
|
230
|
+
}
|
|
231
|
+
const result = await client.setPostApproval(input.post_id, {
|
|
232
|
+
action: input.approval_action,
|
|
233
|
+
axis: input.approval_axis,
|
|
234
|
+
note: input.approval_note,
|
|
235
|
+
});
|
|
236
|
+
return JSON.stringify(result, null, 2);
|
|
237
|
+
}
|
|
212
238
|
async function updateReleaseId(client, input) {
|
|
213
239
|
const result = await client.updatePostReleaseId(input.post_id, {
|
|
214
240
|
release_id: input.release_id,
|
|
@@ -494,11 +494,11 @@ export declare const validatePostInputSchema: import("zod").ZodObject<{
|
|
|
494
494
|
username?: string | undefined;
|
|
495
495
|
workspace_id?: string | undefined;
|
|
496
496
|
account_id?: string | undefined;
|
|
497
|
+
caption?: string | undefined;
|
|
497
498
|
scheduled_at?: string | undefined;
|
|
498
499
|
media_url?: string | undefined;
|
|
499
500
|
media_urls?: string[] | undefined;
|
|
500
501
|
post_type?: string | undefined;
|
|
501
|
-
caption?: string | undefined;
|
|
502
502
|
thread_posts?: string[] | undefined;
|
|
503
503
|
instagram_settings?: {
|
|
504
504
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -657,11 +657,11 @@ export declare const validatePostInputSchema: import("zod").ZodObject<{
|
|
|
657
657
|
username?: string | undefined;
|
|
658
658
|
workspace_id?: string | undefined;
|
|
659
659
|
account_id?: string | undefined;
|
|
660
|
+
caption?: string | undefined;
|
|
660
661
|
scheduled_at?: string | undefined;
|
|
661
662
|
media_url?: string | undefined;
|
|
662
663
|
media_urls?: string[] | undefined;
|
|
663
664
|
post_type?: string | undefined;
|
|
664
|
-
caption?: string | undefined;
|
|
665
665
|
thread_posts?: string[] | undefined;
|
|
666
666
|
instagram_settings?: {
|
|
667
667
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -1314,11 +1314,11 @@ export declare const validatePostTool: {
|
|
|
1314
1314
|
username?: string | undefined;
|
|
1315
1315
|
workspace_id?: string | undefined;
|
|
1316
1316
|
account_id?: string | undefined;
|
|
1317
|
+
caption?: string | undefined;
|
|
1317
1318
|
scheduled_at?: string | undefined;
|
|
1318
1319
|
media_url?: string | undefined;
|
|
1319
1320
|
media_urls?: string[] | undefined;
|
|
1320
1321
|
post_type?: string | undefined;
|
|
1321
|
-
caption?: string | undefined;
|
|
1322
1322
|
thread_posts?: string[] | undefined;
|
|
1323
1323
|
instagram_settings?: {
|
|
1324
1324
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
|
@@ -1477,11 +1477,11 @@ export declare const validatePostTool: {
|
|
|
1477
1477
|
username?: string | undefined;
|
|
1478
1478
|
workspace_id?: string | undefined;
|
|
1479
1479
|
account_id?: string | undefined;
|
|
1480
|
+
caption?: string | undefined;
|
|
1480
1481
|
scheduled_at?: string | undefined;
|
|
1481
1482
|
media_url?: string | undefined;
|
|
1482
1483
|
media_urls?: string[] | undefined;
|
|
1483
1484
|
post_type?: string | undefined;
|
|
1484
|
-
caption?: string | undefined;
|
|
1485
1485
|
thread_posts?: string[] | undefined;
|
|
1486
1486
|
instagram_settings?: {
|
|
1487
1487
|
post_type?: "post" | "feed" | "story" | "reel" | "carousel" | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "posterly-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.0",
|
|
4
4
|
"mcpName": "io.github.awpthorp/posterly",
|
|
5
5
|
"description": "MCP server for posterly: schedule and publish social media posts across 18 platforms from any MCP client (Claude, ChatGPT, Cursor, Windsurf, Cline, and more)",
|
|
6
6
|
"license": "MIT",
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.awpthorp/posterly",
|
|
4
4
|
"title": "posterly",
|
|
5
5
|
"description": "Validate, schedule, publish, and analyze social content across 18 platforms with posterly.",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.47.0",
|
|
7
7
|
"websiteUrl": "https://www.poster.ly/mcp",
|
|
8
8
|
"repository": {
|
|
9
9
|
"url": "https://github.com/awpthorp/posterly",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
{
|
|
15
15
|
"registryType": "npm",
|
|
16
16
|
"identifier": "posterly-mcp-server",
|
|
17
|
-
"version": "0.
|
|
17
|
+
"version": "0.47.0",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
|
20
20
|
},
|
package/src/lib/api-client.ts
CHANGED
|
@@ -190,6 +190,7 @@ export interface ApiPostApproval {
|
|
|
190
190
|
asset_status: ApiPostApprovalStatus;
|
|
191
191
|
caption_status: ApiPostApprovalStatus;
|
|
192
192
|
waiting_on: 'client' | 'team' | null;
|
|
193
|
+
pending_client_approval: boolean;
|
|
193
194
|
requested_at: string | null;
|
|
194
195
|
completed_at: string | null;
|
|
195
196
|
}
|
|
@@ -1544,6 +1545,7 @@ export class PosterlyClient {
|
|
|
1544
1545
|
offset?: number;
|
|
1545
1546
|
workspace_id?: string;
|
|
1546
1547
|
approval_status?: string;
|
|
1548
|
+
approval_waiting_on?: string;
|
|
1547
1549
|
brand_id?: string;
|
|
1548
1550
|
}): Promise<{ posts: Post[]; total: number }> {
|
|
1549
1551
|
const searchParams = new URLSearchParams();
|
|
@@ -1554,6 +1556,7 @@ export class PosterlyClient {
|
|
|
1554
1556
|
if (params?.offset) searchParams.set('offset', String(params.offset));
|
|
1555
1557
|
if (params?.workspace_id) searchParams.set('workspace_id', params.workspace_id);
|
|
1556
1558
|
if (params?.approval_status) searchParams.set('approval_status', params.approval_status);
|
|
1559
|
+
if (params?.approval_waiting_on) searchParams.set('approval_waiting_on', params.approval_waiting_on);
|
|
1557
1560
|
if (params?.brand_id) searchParams.set('brand_id', params.brand_id);
|
|
1558
1561
|
|
|
1559
1562
|
const qs = searchParams.toString();
|
|
@@ -1614,8 +1617,27 @@ export class PosterlyClient {
|
|
|
1614
1617
|
return this.request('PATCH', `/posts/${id}`, data);
|
|
1615
1618
|
}
|
|
1616
1619
|
|
|
1617
|
-
async deletePost(
|
|
1618
|
-
|
|
1620
|
+
async deletePost(
|
|
1621
|
+
id: number,
|
|
1622
|
+
options?: { confirmPublished?: boolean },
|
|
1623
|
+
): Promise<{ deleted: boolean; id: number; platform_delete?: string }> {
|
|
1624
|
+
const qs = options?.confirmPublished ? '?confirm_published=true' : '';
|
|
1625
|
+
return this.request('DELETE', `/posts/${id}${qs}`);
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
async setPostApproval(
|
|
1629
|
+
id: number,
|
|
1630
|
+
data: {
|
|
1631
|
+
action: 'request' | 'approve' | 'request_changes' | 'reject';
|
|
1632
|
+
axis?: 'asset' | 'caption';
|
|
1633
|
+
note?: string;
|
|
1634
|
+
},
|
|
1635
|
+
): Promise<Record<string, unknown>> {
|
|
1636
|
+
return this.request('POST', `/posts/${id}/approval`, {
|
|
1637
|
+
action: data.action,
|
|
1638
|
+
...(data.axis ? { axis: data.axis } : {}),
|
|
1639
|
+
...(data.note ? { note: data.note } : {}),
|
|
1640
|
+
});
|
|
1619
1641
|
}
|
|
1620
1642
|
|
|
1621
1643
|
async updatePostStatus(
|
package/src/lib/version.ts
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
// tool set (minus the intentional pre-auth signup tools that only this stdio
|
|
6
6
|
// package exposes). `npm run check:mcp-parity` enforces both the version match
|
|
7
7
|
// and the tool-list match, and runs in the pre-commit hook.
|
|
8
|
-
export const POSTERLY_MCP_VERSION = '0.
|
|
8
|
+
export const POSTERLY_MCP_VERSION = '0.47.0';
|
package/src/tools/delete-post.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { previewThenConfirm, validateArgs, type ToolContext } from '../lib/tool-
|
|
|
6
6
|
type DeletePostInput = {
|
|
7
7
|
post_id?: number;
|
|
8
8
|
group_id?: string;
|
|
9
|
+
confirm_published?: boolean;
|
|
9
10
|
confirm?: boolean;
|
|
10
11
|
preview_id?: string;
|
|
11
12
|
};
|
|
@@ -13,10 +14,14 @@ type DeletePostInput = {
|
|
|
13
14
|
export const deletePostTool = {
|
|
14
15
|
name: 'delete_post',
|
|
15
16
|
description:
|
|
16
|
-
'Delete posts. DESTRUCTIVE and IRREVERSIBLE: deleted posts and captions cannot be recovered
|
|
17
|
+
'Delete posts. DESTRUCTIVE and IRREVERSIBLE: deleted posts and captions cannot be recovered. post_id: delete one post; fetch it with `get_post` first, show the user what will be deleted (caption, account, scheduled time), and pass confirm=true only after explicit confirmation ("yes delete it", "remove it"). A post that is currently publishing is never deleted. A published post is taken down only when the user also explicitly confirms the live removal and you pass confirm_published=true. Instagram and Facebook are attempted when the stored id and token allow it. Every other platform, or a live URL with no id, returns platform_delete_unsupported and the posterly row is kept. A published post with no live URL and no platform id is removed from posterly only. group_id: delete every draft, scheduled, failed, or paused post matching a caller-defined group_id, post_group_id, api_group_id, or release_id. A group delete never removes published posts. Preview then confirm: call without confirm to get a preview and preview_id, show it, then call again with confirm=true and that preview_id. Never delete several posts one by one without listing each and confirming the full list.',
|
|
17
18
|
inputSchema: z.object({
|
|
18
19
|
post_id: z.number().optional().describe('Delete this one post.'),
|
|
19
|
-
group_id: z.string().min(1).max(160).optional().describe('Delete every unpublished post in this group or release instead.'),
|
|
20
|
+
group_id: z.string().min(1).max(160).optional().describe('Delete every unpublished post in this group or release instead. Never deletes published posts.'),
|
|
21
|
+
confirm_published: z
|
|
22
|
+
.boolean()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe('With post_id only. True only after the user explicitly confirms taking a live Instagram or Facebook post down. Without it, a published post is refused and kept.'),
|
|
20
25
|
confirm: z
|
|
21
26
|
.boolean()
|
|
22
27
|
.optional()
|
|
@@ -44,7 +49,7 @@ export const deletePostTool = {
|
|
|
44
49
|
// GET /api/v1/posts has no group filter, so the preview says in plain
|
|
45
50
|
// words which posts match instead of listing them.
|
|
46
51
|
describe: async () => [
|
|
47
|
-
`Deletes every draft, scheduled, failed, or paused post whose group_id, post_group_id, api_group_id, or release_id is ${code(args.group_id)}.
|
|
52
|
+
`Deletes every draft, scheduled, failed, or paused post whose group_id, post_group_id, api_group_id, or release_id is ${code(args.group_id)}. A group delete never removes published posts. This cannot be undone.`,
|
|
48
53
|
'To see the posts first, call list_posts and check each post\'s group or release ID.',
|
|
49
54
|
].join('\n\n'),
|
|
50
55
|
run: async () => {
|
|
@@ -53,17 +58,21 @@ export const deletePostTool = {
|
|
|
53
58
|
},
|
|
54
59
|
});
|
|
55
60
|
}
|
|
56
|
-
return deleteOne(client, args as { post_id: number; confirm?: boolean });
|
|
61
|
+
return deleteOne(client, args as { post_id: number; confirm?: boolean; confirm_published?: boolean });
|
|
57
62
|
},
|
|
58
63
|
};
|
|
59
64
|
|
|
60
|
-
async function deleteOne(
|
|
65
|
+
async function deleteOne(
|
|
66
|
+
client: PosterlyClient,
|
|
67
|
+
input: { post_id: number; confirm?: boolean; confirm_published?: boolean },
|
|
68
|
+
): Promise<string> {
|
|
61
69
|
if (input.confirm !== true) {
|
|
62
70
|
throw new Error('Refusing to delete post without confirm=true after explicit user confirmation.');
|
|
63
71
|
}
|
|
64
|
-
const result = await client.deletePost(input.post_id);
|
|
72
|
+
const result = await client.deletePost(input.post_id, { confirmPublished: input.confirm_published === true });
|
|
73
|
+
const platformDelete = result.platform_delete ? ` Platform delete: ${result.platform_delete}.` : '';
|
|
65
74
|
return [
|
|
66
|
-
`Post #${result.id} deleted successfully
|
|
75
|
+
`Post #${result.id} deleted successfully.${platformDelete}`,
|
|
67
76
|
'It has been removed from the posterly queue.',
|
|
68
77
|
`View queue in posterly: ${dashboardUrlForPosts()}`,
|
|
69
78
|
'',
|
|
@@ -29,7 +29,7 @@ type ListAnalyticsInput = {
|
|
|
29
29
|
export const listAnalyticsTool = {
|
|
30
30
|
name: 'list_analytics',
|
|
31
31
|
description:
|
|
32
|
-
'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.',
|
|
32
|
+
'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. Google Business Profile account analytics includes Calls, Website clicks, and Directions along with Profile Views, Search Views, Maps Views, Customer Actions, and Posts. 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.',
|
|
33
33
|
inputSchema: z.object({
|
|
34
34
|
view: z.enum(['accounts', 'posts', 'insights']).describe('accounts, posts, or insights.'),
|
|
35
35
|
account_id: z
|
|
@@ -194,6 +194,9 @@ function accountMetricRows(summary: AccountAnalyticsSummary, platform: string):
|
|
|
194
194
|
['Search Views', formatNumber(metrics.search_views ?? summary.total_reach)],
|
|
195
195
|
['Maps Views', formatNumber(metrics.maps_views ?? summary.total_profile_views)],
|
|
196
196
|
['Customer Actions', formatNumber(metrics.customer_actions ?? summary.total_accounts_engaged)],
|
|
197
|
+
['Calls', formatNumber(metrics.call_clicks ?? summary.total_call_clicks)],
|
|
198
|
+
['Directions', formatNumber(metrics.direction_requests ?? summary.total_direction_requests)],
|
|
199
|
+
['Website clicks', formatNumber(metrics.website_clicks ?? summary.total_website_clicks)],
|
|
197
200
|
['Posts', formatNumber(metrics.posts ?? summary.current_media_count)],
|
|
198
201
|
];
|
|
199
202
|
}
|
package/src/tools/list-posts.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { SUPPORTED_PLATFORM_INPUTS, SUPPORTED_PLATFORM_IDS } from '../generated/
|
|
|
6
6
|
export const listPostsTool = {
|
|
7
7
|
name: 'list_posts',
|
|
8
8
|
description:
|
|
9
|
-
'List upcoming or recent posts. Filter by status (scheduled, published, failed, draft), platform, account_id, workspace_id, approval_status, or brand_id. Returns posts in your workspaces, teammates\' too, per your role there (brand-locked seats only see their assigned brands). If workspace_id is omitted, this searches every workspace you belong to.',
|
|
9
|
+
'List upcoming or recent posts. Filter by status (scheduled, published, failed, draft), platform, account_id, workspace_id, approval_status, approval_waiting_on, or brand_id. approval_waiting_on=client is the same queue as approval_status=pending_client. Each post includes approval.pending_client_approval. Returns posts in your workspaces, teammates\' too, per your role there (brand-locked seats only see their assigned brands). If workspace_id is omitted, this searches every workspace you belong to.',
|
|
10
10
|
inputSchema: z.object({
|
|
11
11
|
status: z
|
|
12
12
|
.string()
|
|
@@ -34,6 +34,10 @@ export const listPostsTool = {
|
|
|
34
34
|
.enum(['none', 'pending_client', 'approved', 'changes_requested', 'rejected'])
|
|
35
35
|
.optional()
|
|
36
36
|
.describe('Filter by client review approval status. pending_client means the post is waiting on the client.'),
|
|
37
|
+
approval_waiting_on: z
|
|
38
|
+
.enum(['client', 'team'])
|
|
39
|
+
.optional()
|
|
40
|
+
.describe('client: approval_status pending_client. team: changes_requested or rejected. Conflicts with a non-overlapping approval_status.'),
|
|
37
41
|
brand_id: z
|
|
38
42
|
.string()
|
|
39
43
|
.optional()
|
|
@@ -49,6 +53,7 @@ export const listPostsTool = {
|
|
|
49
53
|
limit?: number;
|
|
50
54
|
workspace_id?: string;
|
|
51
55
|
approval_status?: string;
|
|
56
|
+
approval_waiting_on?: 'client' | 'team';
|
|
52
57
|
brand_id?: string;
|
|
53
58
|
}
|
|
54
59
|
) {
|
package/src/tools/update-post.ts
CHANGED
|
@@ -127,12 +127,15 @@ type UpdatePostInput = EditInput & {
|
|
|
127
127
|
status?: 'scheduled' | 'paused' | 'draft';
|
|
128
128
|
release_id?: string;
|
|
129
129
|
group_id?: string;
|
|
130
|
+
approval_action?: 'request' | 'approve' | 'request_changes' | 'reject';
|
|
131
|
+
approval_axis?: 'asset' | 'caption';
|
|
132
|
+
approval_note?: string;
|
|
130
133
|
};
|
|
131
134
|
|
|
132
135
|
export const updatePostTool = {
|
|
133
136
|
name: 'update_post',
|
|
134
137
|
description:
|
|
135
|
-
'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' +
|
|
138
|
+
'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' +
|
|
136
139
|
'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.',
|
|
137
140
|
inputSchema: z.object({
|
|
138
141
|
post_id: z.number().describe('The post ID to update'),
|
|
@@ -170,6 +173,18 @@ export const updatePostTool = {
|
|
|
170
173
|
.optional()
|
|
171
174
|
.describe('Set an external release ID instead of editing the post. Send with post_id and optional group_id only.'),
|
|
172
175
|
group_id: z.string().min(1).max(160).optional().describe('With release_id only: an external group/campaign ID.'),
|
|
176
|
+
approval_action: z
|
|
177
|
+
.enum(['request', 'approve', 'request_changes', 'reject'])
|
|
178
|
+
.optional()
|
|
179
|
+
.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.'),
|
|
180
|
+
approval_axis: z
|
|
181
|
+
.enum(['asset', 'caption'])
|
|
182
|
+
.optional()
|
|
183
|
+
.describe('With approval_action approve, request_changes, or reject: which axis to decide. Omit for both. Not allowed with request.'),
|
|
184
|
+
approval_note: z
|
|
185
|
+
.string()
|
|
186
|
+
.optional()
|
|
187
|
+
.describe('Note stored with the approval change. Required for request_changes.'),
|
|
173
188
|
confirm: z.literal(true).describe('Must be true after explicit user confirmation of the proposed change.'),
|
|
174
189
|
}),
|
|
175
190
|
|
|
@@ -193,6 +208,15 @@ export const updatePostTool = {
|
|
|
193
208
|
const { mode, args } = validateArgs(ctx, 'update_post', input);
|
|
194
209
|
if (mode === 'status') return updateStatus(client, args as { post_id: number; status: 'scheduled' | 'paused' | 'draft'; scheduled_at?: string; confirm: boolean });
|
|
195
210
|
if (mode === 'release_id') return updateReleaseId(client, args as { post_id: number; release_id: string; group_id?: string; confirm: true });
|
|
211
|
+
if (mode === 'approval') {
|
|
212
|
+
return updateApproval(client, args as {
|
|
213
|
+
post_id: number;
|
|
214
|
+
approval_action: 'request' | 'approve' | 'request_changes' | 'reject';
|
|
215
|
+
approval_axis?: 'asset' | 'caption';
|
|
216
|
+
approval_note?: string;
|
|
217
|
+
confirm: true;
|
|
218
|
+
});
|
|
219
|
+
}
|
|
196
220
|
return editPost(client, args as EditInput);
|
|
197
221
|
},
|
|
198
222
|
};
|
|
@@ -241,6 +265,27 @@ async function updateStatus(
|
|
|
241
265
|
return `Post #${input.post_id} status changed from ${transition.old_status || 'unknown'} to ${transition.new_status || input.status}.`;
|
|
242
266
|
}
|
|
243
267
|
|
|
268
|
+
async function updateApproval(
|
|
269
|
+
client: PosterlyClient,
|
|
270
|
+
input: {
|
|
271
|
+
post_id: number;
|
|
272
|
+
approval_action: 'request' | 'approve' | 'request_changes' | 'reject';
|
|
273
|
+
approval_axis?: 'asset' | 'caption';
|
|
274
|
+
approval_note?: string;
|
|
275
|
+
confirm: true;
|
|
276
|
+
},
|
|
277
|
+
): Promise<string> {
|
|
278
|
+
if (input.confirm !== true) {
|
|
279
|
+
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.');
|
|
280
|
+
}
|
|
281
|
+
const result = await client.setPostApproval(input.post_id, {
|
|
282
|
+
action: input.approval_action,
|
|
283
|
+
axis: input.approval_axis,
|
|
284
|
+
note: input.approval_note,
|
|
285
|
+
});
|
|
286
|
+
return JSON.stringify(result, null, 2);
|
|
287
|
+
}
|
|
288
|
+
|
|
244
289
|
async function updateReleaseId(
|
|
245
290
|
client: PosterlyClient,
|
|
246
291
|
input: { post_id: number; release_id: string; group_id?: string; confirm: true },
|
package/tool-registry.json
CHANGED
|
@@ -736,6 +736,43 @@
|
|
|
736
736
|
"type": "string"
|
|
737
737
|
}
|
|
738
738
|
}
|
|
739
|
+
},
|
|
740
|
+
"approval": {
|
|
741
|
+
"label": "Request, approve, request changes, or reject client review",
|
|
742
|
+
"destructive": true,
|
|
743
|
+
"idempotent": true,
|
|
744
|
+
"openWorld": false,
|
|
745
|
+
"confirm": "confirm",
|
|
746
|
+
"when": [
|
|
747
|
+
"approval_action"
|
|
748
|
+
],
|
|
749
|
+
"params": {
|
|
750
|
+
"post_id": {
|
|
751
|
+
"type": "integer",
|
|
752
|
+
"min": 1,
|
|
753
|
+
"required": true
|
|
754
|
+
},
|
|
755
|
+
"approval_action": {
|
|
756
|
+
"type": "string",
|
|
757
|
+
"enum": [
|
|
758
|
+
"request",
|
|
759
|
+
"approve",
|
|
760
|
+
"request_changes",
|
|
761
|
+
"reject"
|
|
762
|
+
],
|
|
763
|
+
"required": true
|
|
764
|
+
},
|
|
765
|
+
"approval_axis": {
|
|
766
|
+
"type": "string",
|
|
767
|
+
"enum": [
|
|
768
|
+
"asset",
|
|
769
|
+
"caption"
|
|
770
|
+
]
|
|
771
|
+
},
|
|
772
|
+
"approval_note": {
|
|
773
|
+
"type": "string"
|
|
774
|
+
}
|
|
775
|
+
}
|
|
739
776
|
}
|
|
740
777
|
}
|
|
741
778
|
},
|
|
@@ -760,6 +797,9 @@
|
|
|
760
797
|
"type": "integer",
|
|
761
798
|
"min": 1,
|
|
762
799
|
"required": true
|
|
800
|
+
},
|
|
801
|
+
"confirm_published": {
|
|
802
|
+
"type": "boolean"
|
|
763
803
|
}
|
|
764
804
|
}
|
|
765
805
|
},
|