posterly-mcp-server 0.48.4 → 0.48.5
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 +11 -2
- package/dist/index.js +4 -0
- package/dist/lib/api-client.d.ts +69 -0
- package/dist/lib/api-client.js +19 -1
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/tools/list-brands.d.ts +2 -2
- package/dist/tools/list-publishing-pauses.d.ts +19 -0
- package/dist/tools/list-publishing-pauses.js +81 -0
- package/dist/tools/manage-publishing-pause.d.ts +35 -0
- package/dist/tools/manage-publishing-pause.js +47 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/index.ts +4 -0
- package/src/lib/api-client.ts +78 -0
- package/src/lib/version.ts +1 -1
- package/src/tools/list-publishing-pauses.ts +98 -0
- package/src/tools/manage-publishing-pause.ts +62 -0
- package/tool-annotations.json +5 -0
- package/tool-registry.json +63 -1
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.48.
|
|
126
|
+
`posterly-mcp-server@0.48.5` exposes 64 tools covering 99 actions. The hosted HTTP endpoint exposes the same catalog minus the three signup tools: 61 tools covering 96 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
|
|
|
@@ -321,13 +321,14 @@ Google Business Profile reviews, media, and audits.
|
|
|
321
321
|
|
|
322
322
|
### Admin (`admin`)
|
|
323
323
|
|
|
324
|
-
API keys, OAuth clients, webhooks, and
|
|
324
|
+
API keys, OAuth clients, webhooks, workspace members, and the publishing pause.
|
|
325
325
|
|
|
326
326
|
| Tool | What it does |
|
|
327
327
|
| --- | --- |
|
|
328
328
|
| `create_api_key` | Create an API key |
|
|
329
329
|
| `delete_api_key` | Revoke an API key |
|
|
330
330
|
| `list_workspace_members` | Workspace members |
|
|
331
|
+
| `list_publishing_pauses` | Publishing pause: whether the workspace or any brand is paused, and what you may do |
|
|
331
332
|
| `list_oauth_clients` | OAuth clients |
|
|
332
333
|
| `list_webhooks` | Webhooks |
|
|
333
334
|
| `create_webhook` | Create a webhook |
|
|
@@ -340,6 +341,14 @@ API keys, OAuth clients, webhooks, and workspace members.
|
|
|
340
341
|
| `update` | Change role or brands, or turn a teammate's API and AI access on or off (preview then confirm) |
|
|
341
342
|
| `remove` | Remove a member or invite (preview then confirm) |
|
|
342
343
|
|
|
344
|
+
**`manage_publishing_pause`**: Pause or resume publishing
|
|
345
|
+
|
|
346
|
+
| Action | What it does |
|
|
347
|
+
| --- | --- |
|
|
348
|
+
| `pause` | Pause publishing for the workspace or one brand: call without `confirm` for a preview of what it holds, then `confirm: true` after the user agrees (publisher or higher) |
|
|
349
|
+
| `resume` | Resume publishing: future posts are scheduled again, posts that missed their time stay paused and are listed (admin or higher) |
|
|
350
|
+
| `publish_missed` | Publish missed posts now, with `confirm: true` after the user agrees (admin or higher) |
|
|
351
|
+
|
|
343
352
|
**`manage_oauth_client`**: Manage an OAuth client
|
|
344
353
|
|
|
345
354
|
| Action | What it does |
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,8 @@ import { createApiKeyTool } from './tools/create-api-key.js';
|
|
|
21
21
|
import { deleteApiKeyTool } from './tools/delete-api-key.js';
|
|
22
22
|
import { listWorkspaceMembersTool } from './tools/list-workspace-members.js';
|
|
23
23
|
import { manageWorkspaceMemberTool } from './tools/manage-workspace-member.js';
|
|
24
|
+
import { listPublishingPausesTool } from './tools/list-publishing-pauses.js';
|
|
25
|
+
import { managePublishingPauseTool } from './tools/manage-publishing-pause.js';
|
|
24
26
|
import { getCreditsTool } from './tools/get-credits.js';
|
|
25
27
|
import { getSubscriptionTool } from './tools/get-subscription.js';
|
|
26
28
|
import { cancelSubscriptionTool } from './tools/cancel-subscription.js';
|
|
@@ -89,6 +91,8 @@ const STDIO_TOOLS = [
|
|
|
89
91
|
deleteApiKeyTool,
|
|
90
92
|
listWorkspaceMembersTool,
|
|
91
93
|
manageWorkspaceMemberTool,
|
|
94
|
+
listPublishingPausesTool,
|
|
95
|
+
managePublishingPauseTool,
|
|
92
96
|
getCreditsTool,
|
|
93
97
|
getSubscriptionTool,
|
|
94
98
|
cancelSubscriptionTool,
|
package/dist/lib/api-client.d.ts
CHANGED
|
@@ -425,6 +425,61 @@ export type WorkspaceMemberConfirmFields = {
|
|
|
425
425
|
confirm?: boolean;
|
|
426
426
|
preview_id?: string;
|
|
427
427
|
};
|
|
428
|
+
export type PublishingPauseState = {
|
|
429
|
+
workspace: {
|
|
430
|
+
id: string;
|
|
431
|
+
name: string | null;
|
|
432
|
+
paused: boolean;
|
|
433
|
+
paused_at: string | null;
|
|
434
|
+
};
|
|
435
|
+
brands: Array<{
|
|
436
|
+
id: string;
|
|
437
|
+
name: string | null;
|
|
438
|
+
paused: boolean;
|
|
439
|
+
paused_at: string;
|
|
440
|
+
}>;
|
|
441
|
+
viewer: {
|
|
442
|
+
role: string;
|
|
443
|
+
can_pause_workspace: boolean;
|
|
444
|
+
can_pause_brand: boolean;
|
|
445
|
+
can_resume: boolean;
|
|
446
|
+
can_publish_missed: boolean;
|
|
447
|
+
};
|
|
448
|
+
};
|
|
449
|
+
/**
|
|
450
|
+
* POST /publishing-pause. A pause without confirm: true changes nothing and
|
|
451
|
+
* answers 400 confirmation_required with a preview; the client returns that
|
|
452
|
+
* body (requires_confirmation: true) instead of throwing, so the tool can show
|
|
453
|
+
* it.
|
|
454
|
+
*/
|
|
455
|
+
export type PublishingPauseChangeResponse = {
|
|
456
|
+
ok?: boolean;
|
|
457
|
+
requires_confirmation?: boolean;
|
|
458
|
+
code?: string;
|
|
459
|
+
error?: string;
|
|
460
|
+
action?: 'pause' | 'resume';
|
|
461
|
+
scope?: 'workspace' | 'brand';
|
|
462
|
+
workspace_id?: string;
|
|
463
|
+
workspace_name?: string | null;
|
|
464
|
+
brand_id?: string | null;
|
|
465
|
+
brand_name?: string | null;
|
|
466
|
+
already_paused?: boolean;
|
|
467
|
+
scheduled_posts_to_hold?: number | null;
|
|
468
|
+
held_count?: number;
|
|
469
|
+
resumed_count?: number;
|
|
470
|
+
missed_count?: number;
|
|
471
|
+
missed_post_ids?: number[];
|
|
472
|
+
still_held_count?: number;
|
|
473
|
+
disconnected_count?: number;
|
|
474
|
+
failed_post_ids?: number[];
|
|
475
|
+
message?: string;
|
|
476
|
+
};
|
|
477
|
+
export type PublishMissedResponse = {
|
|
478
|
+
ok: boolean;
|
|
479
|
+
started: number;
|
|
480
|
+
skipped: number;
|
|
481
|
+
message?: string;
|
|
482
|
+
};
|
|
428
483
|
export type CreateApiKeyPayload = {
|
|
429
484
|
name?: string;
|
|
430
485
|
scopes?: ApiKeyScope[];
|
|
@@ -1159,6 +1214,20 @@ export declare class PosterlyClient {
|
|
|
1159
1214
|
member_id?: string;
|
|
1160
1215
|
invite_id?: string;
|
|
1161
1216
|
}, data: WorkspaceMemberConfirmFields): Promise<WorkspaceMemberChangeResponse>;
|
|
1217
|
+
getPublishingPause(params?: {
|
|
1218
|
+
workspace_id?: string;
|
|
1219
|
+
}): Promise<PublishingPauseState>;
|
|
1220
|
+
changePublishingPause(data: {
|
|
1221
|
+
action: 'pause' | 'resume';
|
|
1222
|
+
brand_id?: string;
|
|
1223
|
+
workspace_id?: string;
|
|
1224
|
+
confirm?: boolean;
|
|
1225
|
+
}): Promise<PublishingPauseChangeResponse>;
|
|
1226
|
+
publishMissedPausedPosts(data: {
|
|
1227
|
+
post_ids: number[];
|
|
1228
|
+
workspace_id?: string;
|
|
1229
|
+
confirm: true;
|
|
1230
|
+
}): Promise<PublishMissedResponse>;
|
|
1162
1231
|
getSubscription(): Promise<SubscriptionSummaryResponse>;
|
|
1163
1232
|
getCredits(): Promise<CreditsSummaryResponse>;
|
|
1164
1233
|
cancelSubscription(data: CancelSubscriptionPayload): Promise<CancelSubscriptionResponse>;
|
package/dist/lib/api-client.js
CHANGED
|
@@ -116,7 +116,7 @@ export class PosterlyClient {
|
|
|
116
116
|
throw new Error('posterly access is not installed yet. Use start_signup to begin paid setup, or set POSTERLY_API_KEY after signup.');
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
-
async request(method, path, body) {
|
|
119
|
+
async request(method, path, body, options) {
|
|
120
120
|
this.requireApiKey();
|
|
121
121
|
const url = `${this.baseUrl}/api/v1${path}`;
|
|
122
122
|
const headers = {
|
|
@@ -144,6 +144,11 @@ export class PosterlyClient {
|
|
|
144
144
|
const res = await fetch(url, init);
|
|
145
145
|
if (!res.ok) {
|
|
146
146
|
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
147
|
+
// A write that needs confirm: true answers 400 with a preview; callers
|
|
148
|
+
// that show previews get the body back instead of an error.
|
|
149
|
+
if (options?.acceptConfirmationPreview && res.status === 400 && err?.requires_confirmation === true) {
|
|
150
|
+
return err;
|
|
151
|
+
}
|
|
147
152
|
throw new Error(formatApiError(res, err));
|
|
148
153
|
}
|
|
149
154
|
return res.json();
|
|
@@ -204,6 +209,19 @@ export class PosterlyClient {
|
|
|
204
209
|
const qs = params.toString();
|
|
205
210
|
return this.request('DELETE', `${path}${qs ? `?${qs}` : ''}`);
|
|
206
211
|
}
|
|
212
|
+
async getPublishingPause(params) {
|
|
213
|
+
const query = new URLSearchParams();
|
|
214
|
+
if (params?.workspace_id)
|
|
215
|
+
query.set('workspace_id', params.workspace_id);
|
|
216
|
+
const qs = query.toString();
|
|
217
|
+
return this.request('GET', `/publishing-pause${qs ? `?${qs}` : ''}`);
|
|
218
|
+
}
|
|
219
|
+
async changePublishingPause(data) {
|
|
220
|
+
return this.request('POST', '/publishing-pause', data, { acceptConfirmationPreview: true });
|
|
221
|
+
}
|
|
222
|
+
async publishMissedPausedPosts(data) {
|
|
223
|
+
return this.request('POST', '/publishing-pause/publish-missed', data);
|
|
224
|
+
}
|
|
207
225
|
async getSubscription() {
|
|
208
226
|
return this.request('GET', '/subscription');
|
|
209
227
|
}
|
package/dist/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const POSTERLY_MCP_VERSION = "0.48.
|
|
1
|
+
export declare const POSTERLY_MCP_VERSION = "0.48.5";
|
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.48.
|
|
8
|
+
export const POSTERLY_MCP_VERSION = '0.48.5';
|
|
@@ -16,11 +16,11 @@ export declare const listBrandsTool: {
|
|
|
16
16
|
}, "strip", z.ZodTypeAny, {
|
|
17
17
|
workspace_id?: string | undefined;
|
|
18
18
|
brand_id?: string | undefined;
|
|
19
|
-
view?: "
|
|
19
|
+
view?: "brand" | "accounts" | "brands" | "profile" | undefined;
|
|
20
20
|
}, {
|
|
21
21
|
workspace_id?: string | undefined;
|
|
22
22
|
brand_id?: string | undefined;
|
|
23
|
-
view?: "
|
|
23
|
+
view?: "brand" | "accounts" | "brands" | "profile" | undefined;
|
|
24
24
|
}>;
|
|
25
25
|
/** The old tools' exact input schemas, for alias calls (see alias-transport.ts). */
|
|
26
26
|
legacySchemas: {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient, PublishingPauseChangeResponse, PublishingPauseState, PublishMissedResponse } from '../lib/api-client.js';
|
|
3
|
+
export declare function formatPublishingPauseState(data: PublishingPauseState): string;
|
|
4
|
+
export declare function formatPublishingPauseChange(data: PublishingPauseChangeResponse): string;
|
|
5
|
+
export declare function formatPublishMissed(data: PublishMissedResponse): string;
|
|
6
|
+
export declare const listPublishingPausesTool: {
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
inputSchema: z.ZodObject<{
|
|
10
|
+
workspace_id: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
workspace_id?: string | undefined;
|
|
13
|
+
}, {
|
|
14
|
+
workspace_id?: string | undefined;
|
|
15
|
+
}>;
|
|
16
|
+
execute(client: PosterlyClient, input: {
|
|
17
|
+
workspace_id?: string;
|
|
18
|
+
}): Promise<string>;
|
|
19
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// Plain-text output shared by list_publishing_pauses and
|
|
3
|
+
// manage_publishing_pause. The hosted endpoint (lib/mcp/http-tools.ts) renders
|
|
4
|
+
// the same text from the same API responses; scripts/test-publishing-pause-api.ts
|
|
5
|
+
// checks the two surfaces match.
|
|
6
|
+
function yesNo(value) {
|
|
7
|
+
return value ? 'yes' : 'no';
|
|
8
|
+
}
|
|
9
|
+
function idList(ids) {
|
|
10
|
+
return Array.isArray(ids) && ids.length > 0 ? ids.join(', ') : 'none';
|
|
11
|
+
}
|
|
12
|
+
function scopeText(data) {
|
|
13
|
+
if (data.scope === 'brand')
|
|
14
|
+
return `brand ${data.brand_name ? `${data.brand_name} ` : ''}(${data.brand_id})`;
|
|
15
|
+
return `the workspace ${data.workspace_name ? `${data.workspace_name} ` : ''}(${data.workspace_id})`;
|
|
16
|
+
}
|
|
17
|
+
export function formatPublishingPauseState(data) {
|
|
18
|
+
const workspace = data.workspace;
|
|
19
|
+
const brands = Array.isArray(data.brands) ? data.brands : [];
|
|
20
|
+
const viewer = data.viewer;
|
|
21
|
+
return [
|
|
22
|
+
`Publishing pause for ${workspace.name || 'this workspace'} (${workspace.id})`,
|
|
23
|
+
`Workspace: ${workspace.paused ? `paused since ${workspace.paused_at}` : 'publishing normally'}`,
|
|
24
|
+
brands.length > 0
|
|
25
|
+
? ['Paused brands:', ...brands.map((brand) => `- ${brand.name || 'Unnamed brand'} (${brand.id}), paused since ${brand.paused_at}`)].join('\n')
|
|
26
|
+
: 'Paused brands: none',
|
|
27
|
+
`Your role: ${viewer.role}. Pause the workspace: ${yesNo(viewer.can_pause_workspace)}. Pause a brand: ${yesNo(viewer.can_pause_brand)}. Resume and publish missed posts: ${yesNo(viewer.can_resume)}.`,
|
|
28
|
+
'While paused, nothing in that scope publishes (scheduled posts are held, Post now is refused). Pause and resume with manage_publishing_pause.',
|
|
29
|
+
].join('\n');
|
|
30
|
+
}
|
|
31
|
+
export function formatPublishingPauseChange(data) {
|
|
32
|
+
if (data.requires_confirmation) {
|
|
33
|
+
return [
|
|
34
|
+
'Preview only: nothing has changed yet.',
|
|
35
|
+
data.error || '',
|
|
36
|
+
`Scope: ${scopeText(data)}`,
|
|
37
|
+
`Scheduled posts to hold: ${typeof data.scheduled_posts_to_hold === 'number' ? data.scheduled_posts_to_hold : 'unknown'}`,
|
|
38
|
+
`Already paused: ${yesNo(data.already_paused)}`,
|
|
39
|
+
'Next step: show this to the user. Only after they agree, call manage_publishing_pause again with action=pause, the same fields, and confirm=true.',
|
|
40
|
+
].filter(Boolean).join('\n');
|
|
41
|
+
}
|
|
42
|
+
if (data.action === 'pause') {
|
|
43
|
+
const failed = data.failed_post_ids || [];
|
|
44
|
+
return [
|
|
45
|
+
`Publishing paused for ${scopeText(data)}.`,
|
|
46
|
+
`Held posts: ${data.held_count ?? 0}`,
|
|
47
|
+
failed.length > 0 ? `Posts that could not be marked as held (the publish guard still holds them): ${idList(failed)}` : '',
|
|
48
|
+
data.message || '',
|
|
49
|
+
].filter(Boolean).join('\n');
|
|
50
|
+
}
|
|
51
|
+
const missed = data.missed_post_ids || [];
|
|
52
|
+
const failed = data.failed_post_ids || [];
|
|
53
|
+
return [
|
|
54
|
+
`Publishing resumed for ${scopeText(data)}.`,
|
|
55
|
+
`Scheduled again: ${data.resumed_count ?? 0}`,
|
|
56
|
+
`Missed their time while paused (still paused): ${missed.length}${missed.length > 0 ? ` [post IDs: ${idList(missed)}]` : ''}`,
|
|
57
|
+
`Still held by another pause: ${data.still_held_count ?? 0}`,
|
|
58
|
+
(data.disconnected_count ?? 0) > 0 ? `Still paused because the account is disconnected: ${data.disconnected_count}` : '',
|
|
59
|
+
failed.length > 0 ? `Could not be released (try resume again): ${idList(failed)}` : '',
|
|
60
|
+
missed.length > 0
|
|
61
|
+
? 'Next step: missed posts never publish on their own. Ask the user whether to publish them now (action=publish_missed with these post_ids and confirm=true) or reschedule them.'
|
|
62
|
+
: '',
|
|
63
|
+
].filter(Boolean).join('\n');
|
|
64
|
+
}
|
|
65
|
+
export function formatPublishMissed(data) {
|
|
66
|
+
return [
|
|
67
|
+
`Missed posts: ${data.started} publishing now, ${data.skipped} skipped.`,
|
|
68
|
+
data.message || '',
|
|
69
|
+
].filter(Boolean).join('\n');
|
|
70
|
+
}
|
|
71
|
+
export const listPublishingPausesTool = {
|
|
72
|
+
name: 'list_publishing_pauses',
|
|
73
|
+
description: 'Is publishing paused? Shows whether the workspace is paused, which brands are paused (only brands you can see), and whether you may pause, resume, or publish missed posts. Read-only. Change it with manage_publishing_pause.',
|
|
74
|
+
inputSchema: z.object({
|
|
75
|
+
workspace_id: z.string().optional().describe('Workspace ID from whoami. Defaults to your default workspace.'),
|
|
76
|
+
}),
|
|
77
|
+
async execute(client, input) {
|
|
78
|
+
const state = await client.getPublishingPause(input.workspace_id ? { workspace_id: input.workspace_id } : undefined);
|
|
79
|
+
return formatPublishingPauseState(state);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
import { type ToolContext } from '../lib/tool-runtime.js';
|
|
4
|
+
type ManagePublishingPauseInput = {
|
|
5
|
+
action: 'pause' | 'resume' | 'publish_missed';
|
|
6
|
+
workspace_id?: string;
|
|
7
|
+
brand_id?: string;
|
|
8
|
+
post_ids?: number[];
|
|
9
|
+
confirm?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare const managePublishingPauseTool: {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
inputSchema: z.ZodObject<{
|
|
15
|
+
action: z.ZodEnum<["pause", "resume", "publish_missed"]>;
|
|
16
|
+
workspace_id: z.ZodOptional<z.ZodString>;
|
|
17
|
+
brand_id: z.ZodOptional<z.ZodString>;
|
|
18
|
+
post_ids: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
|
|
19
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
}, "strip", z.ZodTypeAny, {
|
|
21
|
+
action: "pause" | "resume" | "publish_missed";
|
|
22
|
+
confirm?: boolean | undefined;
|
|
23
|
+
workspace_id?: string | undefined;
|
|
24
|
+
brand_id?: string | undefined;
|
|
25
|
+
post_ids?: number[] | undefined;
|
|
26
|
+
}, {
|
|
27
|
+
action: "pause" | "resume" | "publish_missed";
|
|
28
|
+
confirm?: boolean | undefined;
|
|
29
|
+
workspace_id?: string | undefined;
|
|
30
|
+
brand_id?: string | undefined;
|
|
31
|
+
post_ids?: number[] | undefined;
|
|
32
|
+
}>;
|
|
33
|
+
execute(client: PosterlyClient, input: ManagePublishingPauseInput, ctx: ToolContext): Promise<string>;
|
|
34
|
+
};
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { validateArgs } from '../lib/tool-runtime.js';
|
|
3
|
+
import { formatPublishingPauseChange, formatPublishMissed } from './list-publishing-pauses.js';
|
|
4
|
+
/** Same wording as EMPTY_BRAND_ID_MESSAGE in lib/publish/publishing-pause-service.ts. */
|
|
5
|
+
const EMPTY_BRAND_ID_MESSAGE = 'brand_id is empty; omit it to target the whole workspace.';
|
|
6
|
+
export const managePublishingPauseTool = {
|
|
7
|
+
name: 'manage_publishing_pause',
|
|
8
|
+
description: 'Pause or resume publishing for a whole workspace or one brand (check it first with list_publishing_pauses). Only act when the user asks; never pause on your own initiative. action=pause (optional brand_id; leave it out for the whole workspace): holds every scheduled post in that scope and blocks Post now until an admin resumes. Call it first without confirm to get a preview (scope and how many scheduled posts it holds), show it to the user, and only after they agree call again with confirm=true. Publisher or higher; a seat limited to some brands can only pause those brands. action=resume (optional brand_id; admin or higher): future posts are scheduled again; posts whose time passed while paused stay paused and are listed as missed. Missed posts never publish on their own. action=publish_missed (post_ids from the resume, confirm=true after the user agrees; admin or higher): publishes those missed posts now.',
|
|
9
|
+
inputSchema: z.object({
|
|
10
|
+
action: z.enum(['pause', 'resume', 'publish_missed']).describe('pause, resume, or publish_missed.'),
|
|
11
|
+
workspace_id: z.string().optional().describe('Workspace ID from whoami. Defaults to your default workspace.'),
|
|
12
|
+
brand_id: z.string().optional().describe('pause and resume: a brand ID from list_brands. Leave it out (never empty) for the whole workspace.'),
|
|
13
|
+
post_ids: z.array(z.number().int().positive()).max(500).optional().describe('publish_missed: the missed post IDs a resume returned.'),
|
|
14
|
+
confirm: z.boolean().optional().describe('pause: leave out to get a preview; true only after the user agreed. publish_missed: true after the user agreed. resume: not needed.'),
|
|
15
|
+
}),
|
|
16
|
+
async execute(client, input, ctx) {
|
|
17
|
+
// A present but empty brand_id is refused, never read as the whole
|
|
18
|
+
// workspace (same rule and wording as the API and the hosted endpoint).
|
|
19
|
+
if ((input?.action === 'pause' || input?.action === 'resume') && input && Object.prototype.hasOwnProperty.call(input, 'brand_id')) {
|
|
20
|
+
const value = input.brand_id;
|
|
21
|
+
if (value === null || (typeof value === 'string' && value.trim() === ''))
|
|
22
|
+
throw new Error(EMPTY_BRAND_ID_MESSAGE);
|
|
23
|
+
}
|
|
24
|
+
const { mode, args } = validateArgs(ctx, 'manage_publishing_pause', input);
|
|
25
|
+
const call = args;
|
|
26
|
+
const workspace = call.workspace_id ? { workspace_id: call.workspace_id } : {};
|
|
27
|
+
if (mode === 'publish_missed') {
|
|
28
|
+
if (call.confirm !== true) {
|
|
29
|
+
throw new Error('Refusing to publish missed posts without confirm=true after explicit user confirmation.');
|
|
30
|
+
}
|
|
31
|
+
const result = await client.publishMissedPausedPosts({ ...workspace, post_ids: call.post_ids || [], confirm: true });
|
|
32
|
+
return formatPublishMissed(result);
|
|
33
|
+
}
|
|
34
|
+
const brand = call.brand_id ? { brand_id: call.brand_id } : {};
|
|
35
|
+
if (mode === 'pause') {
|
|
36
|
+
const result = await client.changePublishingPause({
|
|
37
|
+
action: 'pause',
|
|
38
|
+
...workspace,
|
|
39
|
+
...brand,
|
|
40
|
+
...(call.confirm === true ? { confirm: true } : {}),
|
|
41
|
+
});
|
|
42
|
+
return formatPublishingPauseChange(result);
|
|
43
|
+
}
|
|
44
|
+
const result = await client.changePublishingPause({ action: 'resume', ...workspace, ...brand });
|
|
45
|
+
return formatPublishingPauseChange(result);
|
|
46
|
+
},
|
|
47
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "posterly-mcp-server",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.5",
|
|
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.48.
|
|
6
|
+
"version": "0.48.5",
|
|
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.48.
|
|
17
|
+
"version": "0.48.5",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
|
20
20
|
},
|
package/src/index.ts
CHANGED
|
@@ -24,6 +24,8 @@ import { createApiKeyTool } from './tools/create-api-key.js';
|
|
|
24
24
|
import { deleteApiKeyTool } from './tools/delete-api-key.js';
|
|
25
25
|
import { listWorkspaceMembersTool } from './tools/list-workspace-members.js';
|
|
26
26
|
import { manageWorkspaceMemberTool } from './tools/manage-workspace-member.js';
|
|
27
|
+
import { listPublishingPausesTool } from './tools/list-publishing-pauses.js';
|
|
28
|
+
import { managePublishingPauseTool } from './tools/manage-publishing-pause.js';
|
|
27
29
|
import { getCreditsTool } from './tools/get-credits.js';
|
|
28
30
|
import { getSubscriptionTool } from './tools/get-subscription.js';
|
|
29
31
|
import { cancelSubscriptionTool } from './tools/cancel-subscription.js';
|
|
@@ -108,6 +110,8 @@ const STDIO_TOOLS: ToolModule[] = [
|
|
|
108
110
|
deleteApiKeyTool,
|
|
109
111
|
listWorkspaceMembersTool,
|
|
110
112
|
manageWorkspaceMemberTool,
|
|
113
|
+
listPublishingPausesTool,
|
|
114
|
+
managePublishingPauseTool,
|
|
111
115
|
getCreditsTool,
|
|
112
116
|
getSubscriptionTool,
|
|
113
117
|
cancelSubscriptionTool,
|
package/src/lib/api-client.ts
CHANGED
|
@@ -469,6 +469,54 @@ export type WorkspaceMemberConfirmFields = {
|
|
|
469
469
|
preview_id?: string;
|
|
470
470
|
};
|
|
471
471
|
|
|
472
|
+
export type PublishingPauseState = {
|
|
473
|
+
workspace: { id: string; name: string | null; paused: boolean; paused_at: string | null };
|
|
474
|
+
brands: Array<{ id: string; name: string | null; paused: boolean; paused_at: string }>;
|
|
475
|
+
viewer: {
|
|
476
|
+
role: string;
|
|
477
|
+
can_pause_workspace: boolean;
|
|
478
|
+
can_pause_brand: boolean;
|
|
479
|
+
can_resume: boolean;
|
|
480
|
+
can_publish_missed: boolean;
|
|
481
|
+
};
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* POST /publishing-pause. A pause without confirm: true changes nothing and
|
|
486
|
+
* answers 400 confirmation_required with a preview; the client returns that
|
|
487
|
+
* body (requires_confirmation: true) instead of throwing, so the tool can show
|
|
488
|
+
* it.
|
|
489
|
+
*/
|
|
490
|
+
export type PublishingPauseChangeResponse = {
|
|
491
|
+
ok?: boolean;
|
|
492
|
+
requires_confirmation?: boolean;
|
|
493
|
+
code?: string;
|
|
494
|
+
error?: string;
|
|
495
|
+
action?: 'pause' | 'resume';
|
|
496
|
+
scope?: 'workspace' | 'brand';
|
|
497
|
+
workspace_id?: string;
|
|
498
|
+
workspace_name?: string | null;
|
|
499
|
+
brand_id?: string | null;
|
|
500
|
+
brand_name?: string | null;
|
|
501
|
+
already_paused?: boolean;
|
|
502
|
+
scheduled_posts_to_hold?: number | null;
|
|
503
|
+
held_count?: number;
|
|
504
|
+
resumed_count?: number;
|
|
505
|
+
missed_count?: number;
|
|
506
|
+
missed_post_ids?: number[];
|
|
507
|
+
still_held_count?: number;
|
|
508
|
+
disconnected_count?: number;
|
|
509
|
+
failed_post_ids?: number[];
|
|
510
|
+
message?: string;
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
export type PublishMissedResponse = {
|
|
514
|
+
ok: boolean;
|
|
515
|
+
started: number;
|
|
516
|
+
skipped: number;
|
|
517
|
+
message?: string;
|
|
518
|
+
};
|
|
519
|
+
|
|
472
520
|
export type CreateApiKeyPayload = {
|
|
473
521
|
name?: string;
|
|
474
522
|
scopes?: ApiKeyScope[];
|
|
@@ -1290,6 +1338,7 @@ export class PosterlyClient {
|
|
|
1290
1338
|
method: string,
|
|
1291
1339
|
path: string,
|
|
1292
1340
|
body?: unknown,
|
|
1341
|
+
options?: { acceptConfirmationPreview?: boolean },
|
|
1293
1342
|
): Promise<T> {
|
|
1294
1343
|
this.requireApiKey();
|
|
1295
1344
|
|
|
@@ -1322,6 +1371,11 @@ export class PosterlyClient {
|
|
|
1322
1371
|
|
|
1323
1372
|
if (!res.ok) {
|
|
1324
1373
|
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
1374
|
+
// A write that needs confirm: true answers 400 with a preview; callers
|
|
1375
|
+
// that show previews get the body back instead of an error.
|
|
1376
|
+
if (options?.acceptConfirmationPreview && res.status === 400 && err?.requires_confirmation === true) {
|
|
1377
|
+
return err as T;
|
|
1378
|
+
}
|
|
1325
1379
|
throw new Error(formatApiError(res, err));
|
|
1326
1380
|
}
|
|
1327
1381
|
|
|
@@ -1411,6 +1465,30 @@ export class PosterlyClient {
|
|
|
1411
1465
|
return this.request('DELETE', `${path}${qs ? `?${qs}` : ''}`);
|
|
1412
1466
|
}
|
|
1413
1467
|
|
|
1468
|
+
async getPublishingPause(params?: { workspace_id?: string }): Promise<PublishingPauseState> {
|
|
1469
|
+
const query = new URLSearchParams();
|
|
1470
|
+
if (params?.workspace_id) query.set('workspace_id', params.workspace_id);
|
|
1471
|
+
const qs = query.toString();
|
|
1472
|
+
return this.request('GET', `/publishing-pause${qs ? `?${qs}` : ''}`);
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
async changePublishingPause(data: {
|
|
1476
|
+
action: 'pause' | 'resume';
|
|
1477
|
+
brand_id?: string;
|
|
1478
|
+
workspace_id?: string;
|
|
1479
|
+
confirm?: boolean;
|
|
1480
|
+
}): Promise<PublishingPauseChangeResponse> {
|
|
1481
|
+
return this.request('POST', '/publishing-pause', data, { acceptConfirmationPreview: true });
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
async publishMissedPausedPosts(data: {
|
|
1485
|
+
post_ids: number[];
|
|
1486
|
+
workspace_id?: string;
|
|
1487
|
+
confirm: true;
|
|
1488
|
+
}): Promise<PublishMissedResponse> {
|
|
1489
|
+
return this.request('POST', '/publishing-pause/publish-missed', data);
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1414
1492
|
async getSubscription(): Promise<SubscriptionSummaryResponse> {
|
|
1415
1493
|
return this.request('GET', '/subscription');
|
|
1416
1494
|
}
|
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.48.
|
|
8
|
+
export const POSTERLY_MCP_VERSION = '0.48.5';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type {
|
|
3
|
+
PosterlyClient,
|
|
4
|
+
PublishingPauseChangeResponse,
|
|
5
|
+
PublishingPauseState,
|
|
6
|
+
PublishMissedResponse,
|
|
7
|
+
} from '../lib/api-client.js';
|
|
8
|
+
|
|
9
|
+
// Plain-text output shared by list_publishing_pauses and
|
|
10
|
+
// manage_publishing_pause. The hosted endpoint (lib/mcp/http-tools.ts) renders
|
|
11
|
+
// the same text from the same API responses; scripts/test-publishing-pause-api.ts
|
|
12
|
+
// checks the two surfaces match.
|
|
13
|
+
|
|
14
|
+
function yesNo(value: unknown): string {
|
|
15
|
+
return value ? 'yes' : 'no';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function idList(ids: number[] | undefined): string {
|
|
19
|
+
return Array.isArray(ids) && ids.length > 0 ? ids.join(', ') : 'none';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function scopeText(data: { scope?: string; workspace_id?: string; brand_id?: string | null; brand_name?: string | null; workspace_name?: string | null }): string {
|
|
23
|
+
if (data.scope === 'brand') return `brand ${data.brand_name ? `${data.brand_name} ` : ''}(${data.brand_id})`;
|
|
24
|
+
return `the workspace ${data.workspace_name ? `${data.workspace_name} ` : ''}(${data.workspace_id})`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function formatPublishingPauseState(data: PublishingPauseState): string {
|
|
28
|
+
const workspace = data.workspace;
|
|
29
|
+
const brands = Array.isArray(data.brands) ? data.brands : [];
|
|
30
|
+
const viewer = data.viewer;
|
|
31
|
+
return [
|
|
32
|
+
`Publishing pause for ${workspace.name || 'this workspace'} (${workspace.id})`,
|
|
33
|
+
`Workspace: ${workspace.paused ? `paused since ${workspace.paused_at}` : 'publishing normally'}`,
|
|
34
|
+
brands.length > 0
|
|
35
|
+
? ['Paused brands:', ...brands.map((brand) => `- ${brand.name || 'Unnamed brand'} (${brand.id}), paused since ${brand.paused_at}`)].join('\n')
|
|
36
|
+
: 'Paused brands: none',
|
|
37
|
+
`Your role: ${viewer.role}. Pause the workspace: ${yesNo(viewer.can_pause_workspace)}. Pause a brand: ${yesNo(viewer.can_pause_brand)}. Resume and publish missed posts: ${yesNo(viewer.can_resume)}.`,
|
|
38
|
+
'While paused, nothing in that scope publishes (scheduled posts are held, Post now is refused). Pause and resume with manage_publishing_pause.',
|
|
39
|
+
].join('\n');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function formatPublishingPauseChange(data: PublishingPauseChangeResponse): string {
|
|
43
|
+
if (data.requires_confirmation) {
|
|
44
|
+
return [
|
|
45
|
+
'Preview only: nothing has changed yet.',
|
|
46
|
+
data.error || '',
|
|
47
|
+
`Scope: ${scopeText(data)}`,
|
|
48
|
+
`Scheduled posts to hold: ${typeof data.scheduled_posts_to_hold === 'number' ? data.scheduled_posts_to_hold : 'unknown'}`,
|
|
49
|
+
`Already paused: ${yesNo(data.already_paused)}`,
|
|
50
|
+
'Next step: show this to the user. Only after they agree, call manage_publishing_pause again with action=pause, the same fields, and confirm=true.',
|
|
51
|
+
].filter(Boolean).join('\n');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (data.action === 'pause') {
|
|
55
|
+
const failed = data.failed_post_ids || [];
|
|
56
|
+
return [
|
|
57
|
+
`Publishing paused for ${scopeText(data)}.`,
|
|
58
|
+
`Held posts: ${data.held_count ?? 0}`,
|
|
59
|
+
failed.length > 0 ? `Posts that could not be marked as held (the publish guard still holds them): ${idList(failed)}` : '',
|
|
60
|
+
data.message || '',
|
|
61
|
+
].filter(Boolean).join('\n');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const missed = data.missed_post_ids || [];
|
|
65
|
+
const failed = data.failed_post_ids || [];
|
|
66
|
+
return [
|
|
67
|
+
`Publishing resumed for ${scopeText(data)}.`,
|
|
68
|
+
`Scheduled again: ${data.resumed_count ?? 0}`,
|
|
69
|
+
`Missed their time while paused (still paused): ${missed.length}${missed.length > 0 ? ` [post IDs: ${idList(missed)}]` : ''}`,
|
|
70
|
+
`Still held by another pause: ${data.still_held_count ?? 0}`,
|
|
71
|
+
(data.disconnected_count ?? 0) > 0 ? `Still paused because the account is disconnected: ${data.disconnected_count}` : '',
|
|
72
|
+
failed.length > 0 ? `Could not be released (try resume again): ${idList(failed)}` : '',
|
|
73
|
+
missed.length > 0
|
|
74
|
+
? 'Next step: missed posts never publish on their own. Ask the user whether to publish them now (action=publish_missed with these post_ids and confirm=true) or reschedule them.'
|
|
75
|
+
: '',
|
|
76
|
+
].filter(Boolean).join('\n');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function formatPublishMissed(data: PublishMissedResponse): string {
|
|
80
|
+
return [
|
|
81
|
+
`Missed posts: ${data.started} publishing now, ${data.skipped} skipped.`,
|
|
82
|
+
data.message || '',
|
|
83
|
+
].filter(Boolean).join('\n');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const listPublishingPausesTool = {
|
|
87
|
+
name: 'list_publishing_pauses',
|
|
88
|
+
description:
|
|
89
|
+
'Is publishing paused? Shows whether the workspace is paused, which brands are paused (only brands you can see), and whether you may pause, resume, or publish missed posts. Read-only. Change it with manage_publishing_pause.',
|
|
90
|
+
inputSchema: z.object({
|
|
91
|
+
workspace_id: z.string().optional().describe('Workspace ID from whoami. Defaults to your default workspace.'),
|
|
92
|
+
}),
|
|
93
|
+
|
|
94
|
+
async execute(client: PosterlyClient, input: { workspace_id?: string }) {
|
|
95
|
+
const state = await client.getPublishingPause(input.workspace_id ? { workspace_id: input.workspace_id } : undefined);
|
|
96
|
+
return formatPublishingPauseState(state);
|
|
97
|
+
},
|
|
98
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
import { validateArgs, type ToolContext } from '../lib/tool-runtime.js';
|
|
4
|
+
import { formatPublishingPauseChange, formatPublishMissed } from './list-publishing-pauses.js';
|
|
5
|
+
|
|
6
|
+
/** Same wording as EMPTY_BRAND_ID_MESSAGE in lib/publish/publishing-pause-service.ts. */
|
|
7
|
+
const EMPTY_BRAND_ID_MESSAGE = 'brand_id is empty; omit it to target the whole workspace.';
|
|
8
|
+
|
|
9
|
+
type ManagePublishingPauseInput = {
|
|
10
|
+
action: 'pause' | 'resume' | 'publish_missed';
|
|
11
|
+
workspace_id?: string;
|
|
12
|
+
brand_id?: string;
|
|
13
|
+
post_ids?: number[];
|
|
14
|
+
confirm?: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const managePublishingPauseTool = {
|
|
18
|
+
name: 'manage_publishing_pause',
|
|
19
|
+
description:
|
|
20
|
+
'Pause or resume publishing for a whole workspace or one brand (check it first with list_publishing_pauses). Only act when the user asks; never pause on your own initiative. action=pause (optional brand_id; leave it out for the whole workspace): holds every scheduled post in that scope and blocks Post now until an admin resumes. Call it first without confirm to get a preview (scope and how many scheduled posts it holds), show it to the user, and only after they agree call again with confirm=true. Publisher or higher; a seat limited to some brands can only pause those brands. action=resume (optional brand_id; admin or higher): future posts are scheduled again; posts whose time passed while paused stay paused and are listed as missed. Missed posts never publish on their own. action=publish_missed (post_ids from the resume, confirm=true after the user agrees; admin or higher): publishes those missed posts now.',
|
|
21
|
+
inputSchema: z.object({
|
|
22
|
+
action: z.enum(['pause', 'resume', 'publish_missed']).describe('pause, resume, or publish_missed.'),
|
|
23
|
+
workspace_id: z.string().optional().describe('Workspace ID from whoami. Defaults to your default workspace.'),
|
|
24
|
+
brand_id: z.string().optional().describe('pause and resume: a brand ID from list_brands. Leave it out (never empty) for the whole workspace.'),
|
|
25
|
+
post_ids: z.array(z.number().int().positive()).max(500).optional().describe('publish_missed: the missed post IDs a resume returned.'),
|
|
26
|
+
confirm: z.boolean().optional().describe('pause: leave out to get a preview; true only after the user agreed. publish_missed: true after the user agreed. resume: not needed.'),
|
|
27
|
+
}),
|
|
28
|
+
|
|
29
|
+
async execute(client: PosterlyClient, input: ManagePublishingPauseInput, ctx: ToolContext) {
|
|
30
|
+
// A present but empty brand_id is refused, never read as the whole
|
|
31
|
+
// workspace (same rule and wording as the API and the hosted endpoint).
|
|
32
|
+
if ((input?.action === 'pause' || input?.action === 'resume') && input && Object.prototype.hasOwnProperty.call(input, 'brand_id')) {
|
|
33
|
+
const value = (input as { brand_id?: unknown }).brand_id;
|
|
34
|
+
if (value === null || (typeof value === 'string' && value.trim() === '')) throw new Error(EMPTY_BRAND_ID_MESSAGE);
|
|
35
|
+
}
|
|
36
|
+
const { mode, args } = validateArgs(ctx, 'manage_publishing_pause', input);
|
|
37
|
+
const call = args as ManagePublishingPauseInput;
|
|
38
|
+
const workspace = call.workspace_id ? { workspace_id: call.workspace_id } : {};
|
|
39
|
+
|
|
40
|
+
if (mode === 'publish_missed') {
|
|
41
|
+
if (call.confirm !== true) {
|
|
42
|
+
throw new Error('Refusing to publish missed posts without confirm=true after explicit user confirmation.');
|
|
43
|
+
}
|
|
44
|
+
const result = await client.publishMissedPausedPosts({ ...workspace, post_ids: call.post_ids || [], confirm: true });
|
|
45
|
+
return formatPublishMissed(result);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const brand = call.brand_id ? { brand_id: call.brand_id } : {};
|
|
49
|
+
if (mode === 'pause') {
|
|
50
|
+
const result = await client.changePublishingPause({
|
|
51
|
+
action: 'pause',
|
|
52
|
+
...workspace,
|
|
53
|
+
...brand,
|
|
54
|
+
...(call.confirm === true ? { confirm: true } : {}),
|
|
55
|
+
});
|
|
56
|
+
return formatPublishingPauseChange(result);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const result = await client.changePublishingPause({ action: 'resume', ...workspace, ...brand });
|
|
60
|
+
return formatPublishingPauseChange(result);
|
|
61
|
+
},
|
|
62
|
+
};
|
package/tool-annotations.json
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"delete_api_key",
|
|
9
9
|
"list_workspace_members",
|
|
10
10
|
"manage_workspace_member",
|
|
11
|
+
"list_publishing_pauses",
|
|
12
|
+
"manage_publishing_pause",
|
|
11
13
|
"list_accounts",
|
|
12
14
|
"disconnect_account",
|
|
13
15
|
"connect_account",
|
|
@@ -68,6 +70,7 @@
|
|
|
68
70
|
"get_signup_session",
|
|
69
71
|
"whoami",
|
|
70
72
|
"list_workspace_members",
|
|
73
|
+
"list_publishing_pauses",
|
|
71
74
|
"list_accounts",
|
|
72
75
|
"list_oauth_clients",
|
|
73
76
|
"list_platforms",
|
|
@@ -108,6 +111,7 @@
|
|
|
108
111
|
"manage_comment",
|
|
109
112
|
"manage_webhook",
|
|
110
113
|
"cancel_subscription",
|
|
114
|
+
"manage_publishing_pause",
|
|
111
115
|
"pause_subscription",
|
|
112
116
|
"downgrade_subscription"
|
|
113
117
|
],
|
|
@@ -128,6 +132,7 @@
|
|
|
128
132
|
"get_signup_session",
|
|
129
133
|
"whoami",
|
|
130
134
|
"manage_workspace_member",
|
|
135
|
+
"manage_publishing_pause",
|
|
131
136
|
"list_accounts",
|
|
132
137
|
"disconnect_account",
|
|
133
138
|
"connect_account",
|
package/tool-registry.json
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"admin": {
|
|
28
28
|
"label": "Admin",
|
|
29
|
-
"description": "API keys, OAuth clients, webhooks, and
|
|
29
|
+
"description": "API keys, OAuth clients, webhooks, workspace members, and the publishing pause."
|
|
30
30
|
},
|
|
31
31
|
"billing": {
|
|
32
32
|
"label": "Billing",
|
|
@@ -175,6 +175,68 @@
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
},
|
|
178
|
+
"list_publishing_pauses": {
|
|
179
|
+
"set": "admin",
|
|
180
|
+
"label": "Publishing pause",
|
|
181
|
+
"readOnly": true,
|
|
182
|
+
"destructive": false,
|
|
183
|
+
"idempotent": true,
|
|
184
|
+
"openWorld": false,
|
|
185
|
+
"confirm": "none"
|
|
186
|
+
},
|
|
187
|
+
"manage_publishing_pause": {
|
|
188
|
+
"set": "admin",
|
|
189
|
+
"label": "Pause or resume publishing",
|
|
190
|
+
"selector": "action",
|
|
191
|
+
"actions": {
|
|
192
|
+
"pause": {
|
|
193
|
+
"label": "Pause publishing",
|
|
194
|
+
"destructive": true,
|
|
195
|
+
"idempotent": true,
|
|
196
|
+
"openWorld": false,
|
|
197
|
+
"confirm": "confirm",
|
|
198
|
+
"params": {
|
|
199
|
+
"workspace_id": {
|
|
200
|
+
"type": "string"
|
|
201
|
+
},
|
|
202
|
+
"brand_id": {
|
|
203
|
+
"type": "string"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
"resume": {
|
|
208
|
+
"label": "Resume publishing",
|
|
209
|
+
"destructive": false,
|
|
210
|
+
"idempotent": true,
|
|
211
|
+
"openWorld": false,
|
|
212
|
+
"confirm": "none",
|
|
213
|
+
"params": {
|
|
214
|
+
"workspace_id": {
|
|
215
|
+
"type": "string"
|
|
216
|
+
},
|
|
217
|
+
"brand_id": {
|
|
218
|
+
"type": "string"
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
"publish_missed": {
|
|
223
|
+
"label": "Publish missed posts now",
|
|
224
|
+
"destructive": false,
|
|
225
|
+
"idempotent": false,
|
|
226
|
+
"openWorld": true,
|
|
227
|
+
"confirm": "confirm",
|
|
228
|
+
"params": {
|
|
229
|
+
"workspace_id": {
|
|
230
|
+
"type": "string"
|
|
231
|
+
},
|
|
232
|
+
"post_ids": {
|
|
233
|
+
"type": "array",
|
|
234
|
+
"required": true
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
},
|
|
178
240
|
"list_accounts": {
|
|
179
241
|
"set": "core",
|
|
180
242
|
"label": "Connected accounts",
|