posterly-mcp-server 0.43.3 → 0.43.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 +18 -18
- package/dist/lib/api-client.d.ts +14 -0
- package/dist/lib/api-client.js +6 -2
- package/dist/lib/format.js +1 -1
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/tools/ask-support.js +3 -3
- package/dist/tools/create-api-key.js +1 -1
- package/dist/tools/create-connect-session.js +3 -3
- package/dist/tools/create-post.js +6 -6
- package/dist/tools/create-posts-batch.js +2 -2
- package/dist/tools/create-webhook.d.ts +3 -3
- package/dist/tools/delete-api-key.js +1 -1
- package/dist/tools/delete-post.js +2 -2
- package/dist/tools/generate-captions.js +1 -1
- package/dist/tools/generate-image.js +1 -1
- package/dist/tools/get-agent-signup-info.js +8 -8
- package/dist/tools/get-comment.js +1 -1
- package/dist/tools/get-connect-link.js +1 -1
- package/dist/tools/get-connect-session.js +4 -4
- package/dist/tools/get-mcp-status.js +4 -4
- package/dist/tools/get-post.js +1 -1
- package/dist/tools/get-signup-session.js +7 -7
- package/dist/tools/list-accounts.js +1 -1
- package/dist/tools/list-brands.js +1 -1
- package/dist/tools/list-comments.d.ts +4 -4
- package/dist/tools/list-comments.js +3 -3
- package/dist/tools/list-platforms.js +1 -1
- package/dist/tools/list-posts.d.ts +8 -0
- package/dist/tools/list-posts.js +24 -5
- package/dist/tools/reply-to-comment.js +1 -1
- package/dist/tools/start-signup.js +11 -11
- package/dist/tools/submit-agent-feedback.d.ts +4 -4
- package/dist/tools/submit-product-feedback.d.ts +4 -4
- package/dist/tools/sync-inbox.js +2 -2
- package/dist/tools/update-post.js +2 -2
- package/dist/tools/update-webhook.d.ts +3 -3
- package/dist/tools/upload-media-from-url.js +1 -1
- package/dist/tools/webhook-events.d.ts +2 -2
- package/dist/tools/webhook-events.js +5 -0
- package/dist/tools/whoami.js +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/lib/api-client.ts +20 -2
- package/src/lib/format.ts +1 -1
- package/src/lib/version.ts +1 -1
- package/src/tools/ask-support.ts +3 -3
- package/src/tools/create-api-key.ts +1 -1
- package/src/tools/create-connect-session.ts +3 -3
- package/src/tools/create-post.ts +6 -6
- package/src/tools/create-posts-batch.ts +2 -2
- package/src/tools/delete-api-key.ts +1 -1
- package/src/tools/delete-post.ts +2 -2
- package/src/tools/generate-captions.ts +1 -1
- package/src/tools/generate-image.ts +1 -1
- package/src/tools/get-agent-signup-info.ts +8 -8
- package/src/tools/get-comment.ts +1 -1
- package/src/tools/get-connect-link.ts +1 -1
- package/src/tools/get-connect-session.ts +4 -4
- package/src/tools/get-mcp-status.ts +4 -4
- package/src/tools/get-post.ts +1 -1
- package/src/tools/get-signup-session.ts +7 -7
- package/src/tools/list-accounts.ts +1 -1
- package/src/tools/list-brands.ts +1 -1
- package/src/tools/list-comments.ts +4 -4
- package/src/tools/list-platforms.ts +1 -1
- package/src/tools/list-posts.ts +23 -5
- package/src/tools/reply-to-comment.ts +1 -1
- package/src/tools/start-signup.ts +11 -11
- package/src/tools/sync-inbox.ts +2 -2
- package/src/tools/update-post.ts +2 -2
- package/src/tools/upload-media-from-url.ts +1 -1
- package/src/tools/webhook-events.ts +5 -0
- package/src/tools/whoami.ts +1 -1
|
@@ -14,7 +14,7 @@ export const listBrandsTool = {
|
|
|
14
14
|
if (brands.length === 0) {
|
|
15
15
|
return input.workspace_id
|
|
16
16
|
? mdEmpty('brands', 'This workspace does not have any brands or clients yet.')
|
|
17
|
-
: mdEmpty('brands', 'No brands or clients were found for this API key.', 'Create a brand in the
|
|
17
|
+
: mdEmpty('brands', 'No brands or clients were found for this API key.', 'Create a brand in the posterly dashboard, then run this again.');
|
|
18
18
|
}
|
|
19
19
|
return [
|
|
20
20
|
mdTitle(`Brands (${brands.length})`),
|
|
@@ -6,13 +6,13 @@ export declare const listCommentsTool: {
|
|
|
6
6
|
inputSchema: z.ZodObject<{
|
|
7
7
|
workspace_id: z.ZodOptional<z.ZodString>;
|
|
8
8
|
account_id: z.ZodOptional<z.ZodNumber>;
|
|
9
|
-
platform: z.ZodOptional<z.ZodEnum<["instagram", "facebook", "threads"]>>;
|
|
9
|
+
platform: z.ZodOptional<z.ZodEnum<["instagram", "facebook", "threads", "linkedin"]>>;
|
|
10
10
|
filter: z.ZodOptional<z.ZodEnum<["all", "unread", "flagged"]>>;
|
|
11
11
|
search: z.ZodOptional<z.ZodString>;
|
|
12
12
|
limit: z.ZodOptional<z.ZodNumber>;
|
|
13
13
|
offset: z.ZodOptional<z.ZodNumber>;
|
|
14
14
|
}, "strip", z.ZodTypeAny, {
|
|
15
|
-
platform?: "instagram" | "facebook" | "threads" | undefined;
|
|
15
|
+
platform?: "instagram" | "facebook" | "threads" | "linkedin" | undefined;
|
|
16
16
|
workspace_id?: string | undefined;
|
|
17
17
|
account_id?: number | undefined;
|
|
18
18
|
limit?: number | undefined;
|
|
@@ -20,7 +20,7 @@ export declare const listCommentsTool: {
|
|
|
20
20
|
filter?: "all" | "unread" | "flagged" | undefined;
|
|
21
21
|
search?: string | undefined;
|
|
22
22
|
}, {
|
|
23
|
-
platform?: "instagram" | "facebook" | "threads" | undefined;
|
|
23
|
+
platform?: "instagram" | "facebook" | "threads" | "linkedin" | undefined;
|
|
24
24
|
workspace_id?: string | undefined;
|
|
25
25
|
account_id?: number | undefined;
|
|
26
26
|
limit?: number | undefined;
|
|
@@ -31,7 +31,7 @@ export declare const listCommentsTool: {
|
|
|
31
31
|
execute(client: PosterlyClient, input: {
|
|
32
32
|
workspace_id?: string;
|
|
33
33
|
account_id?: number;
|
|
34
|
-
platform?: "instagram" | "facebook" | "threads";
|
|
34
|
+
platform?: "instagram" | "facebook" | "threads" | "linkedin";
|
|
35
35
|
filter?: "all" | "unread" | "flagged";
|
|
36
36
|
search?: string;
|
|
37
37
|
limit?: number;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { truncateText } from '../lib/format.js';
|
|
3
|
-
const INBOX_PLATFORMS = ['instagram', 'facebook', 'threads'];
|
|
3
|
+
const INBOX_PLATFORMS = ['instagram', 'facebook', 'threads', 'linkedin'];
|
|
4
4
|
export const listCommentsTool = {
|
|
5
5
|
name: 'list_comments',
|
|
6
|
-
description: 'List social inbox comments and replies for Instagram, Facebook Pages, and
|
|
6
|
+
description: 'List social inbox comments and replies for Instagram, Facebook Pages, Threads, and LinkedIn Pages. Filter by account, platform, unread/flagged, or search. Requires a Pro plan or higher plus posts:read.',
|
|
7
7
|
inputSchema: z.object({
|
|
8
8
|
workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
|
|
9
9
|
account_id: z.number().int().positive().optional().describe('Social account ID from list_accounts.'),
|
|
10
|
-
platform: z.enum(INBOX_PLATFORMS).optional().describe('instagram, facebook, or
|
|
10
|
+
platform: z.enum(INBOX_PLATFORMS).optional().describe('instagram, facebook, threads, or linkedin.'),
|
|
11
11
|
filter: z.enum(['all', 'unread', 'flagged']).optional().describe('Comment filter. Defaults to all.'),
|
|
12
12
|
search: z.string().optional().describe('Search comment content or author username.'),
|
|
13
13
|
limit: z.number().int().min(1).max(100).optional().describe('Number of comments to return. Default 50, max 100.'),
|
|
@@ -15,6 +15,6 @@ export const listPlatformsTool = {
|
|
|
15
15
|
const helperCount = platform.helper_tools?.length || 0;
|
|
16
16
|
return `• ${platform.label} (${platform.id}): ${platform.status}; post types: ${platform.post_types.join(', ') || 'n/a'}; helpers: ${helperCount}; analytics: ${platform.analytics_supported ? 'yes' : 'no'}`;
|
|
17
17
|
});
|
|
18
|
-
return `
|
|
18
|
+
return `posterly platforms (${result.platforms.length}):\n${lines.join('\n')}`;
|
|
19
19
|
},
|
|
20
20
|
};
|
|
@@ -9,18 +9,24 @@ export declare const listPostsTool: {
|
|
|
9
9
|
account_id: z.ZodOptional<z.ZodString>;
|
|
10
10
|
limit: z.ZodOptional<z.ZodNumber>;
|
|
11
11
|
workspace_id: z.ZodOptional<z.ZodString>;
|
|
12
|
+
approval_status: z.ZodOptional<z.ZodEnum<["none", "pending_client", "approved", "changes_requested", "rejected"]>>;
|
|
13
|
+
brand_id: z.ZodOptional<z.ZodString>;
|
|
12
14
|
}, "strip", z.ZodTypeAny, {
|
|
13
15
|
platform?: "instagram" | "instagram-standalone" | "facebook" | "tiktok" | "twitter" | "threads" | "linkedin" | "youtube" | "pinterest" | "google_business" | "x" | "telegram" | "bluesky" | "discord" | "slack" | "mastodon" | "devto" | "hashnode" | "wordpress" | "lemmy" | "gmb" | "google-business" | "google_business_profile" | undefined;
|
|
14
16
|
status?: string | undefined;
|
|
15
17
|
workspace_id?: string | undefined;
|
|
16
18
|
account_id?: string | undefined;
|
|
17
19
|
limit?: number | undefined;
|
|
20
|
+
approval_status?: "none" | "pending_client" | "approved" | "changes_requested" | "rejected" | undefined;
|
|
21
|
+
brand_id?: string | undefined;
|
|
18
22
|
}, {
|
|
19
23
|
platform?: "instagram" | "instagram-standalone" | "facebook" | "tiktok" | "twitter" | "threads" | "linkedin" | "youtube" | "pinterest" | "google_business" | "x" | "telegram" | "bluesky" | "discord" | "slack" | "mastodon" | "devto" | "hashnode" | "wordpress" | "lemmy" | "gmb" | "google-business" | "google_business_profile" | undefined;
|
|
20
24
|
status?: string | undefined;
|
|
21
25
|
workspace_id?: string | undefined;
|
|
22
26
|
account_id?: string | undefined;
|
|
23
27
|
limit?: number | undefined;
|
|
28
|
+
approval_status?: "none" | "pending_client" | "approved" | "changes_requested" | "rejected" | undefined;
|
|
29
|
+
brand_id?: string | undefined;
|
|
24
30
|
}>;
|
|
25
31
|
execute(client: PosterlyClient, input: {
|
|
26
32
|
status?: string;
|
|
@@ -28,5 +34,7 @@ export declare const listPostsTool: {
|
|
|
28
34
|
account_id?: string;
|
|
29
35
|
limit?: number;
|
|
30
36
|
workspace_id?: string;
|
|
37
|
+
approval_status?: string;
|
|
38
|
+
brand_id?: string;
|
|
31
39
|
}): Promise<string>;
|
|
32
40
|
};
|
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, or
|
|
6
|
+
description: 'List upcoming or recent posts. Filter by status (scheduled, published, failed, draft), platform, account_id, workspace_id, approval_status, or brand_id. If workspace_id is omitted, posts across every workspace the caller is a member of are returned.',
|
|
7
7
|
inputSchema: z.object({
|
|
8
8
|
status: z
|
|
9
9
|
.string()
|
|
@@ -27,6 +27,14 @@ export const listPostsTool = {
|
|
|
27
27
|
.string()
|
|
28
28
|
.optional()
|
|
29
29
|
.describe('Filter to posts in a specific workspace (get IDs via whoami).'),
|
|
30
|
+
approval_status: z
|
|
31
|
+
.enum(['none', 'pending_client', 'approved', 'changes_requested', 'rejected'])
|
|
32
|
+
.optional()
|
|
33
|
+
.describe('Filter by client review approval status. pending_client means the post is waiting on the client.'),
|
|
34
|
+
brand_id: z
|
|
35
|
+
.string()
|
|
36
|
+
.optional()
|
|
37
|
+
.describe('Filter to posts assigned to a specific brand/client (the id from list_brands).'),
|
|
30
38
|
}),
|
|
31
39
|
async execute(client, input) {
|
|
32
40
|
const { posts, total } = await client.listPosts({
|
|
@@ -34,19 +42,30 @@ export const listPostsTool = {
|
|
|
34
42
|
limit: input.limit || 20,
|
|
35
43
|
});
|
|
36
44
|
if (posts.length === 0) {
|
|
37
|
-
return 'No posts found matching your criteria.\n\nNext step for the AI agent: tell the user their
|
|
45
|
+
return 'No posts found matching your criteria.\n\nNext step for the AI agent: tell the user their posterly queue is clear for that filter, then ask what they want to schedule next.';
|
|
38
46
|
}
|
|
39
47
|
const lines = posts.map((p) => {
|
|
40
48
|
const date = formatLocalDateTime(p.scheduled_at);
|
|
41
49
|
const caption = truncateText(p.content, 60);
|
|
42
|
-
|
|
50
|
+
const approvalBits = [];
|
|
51
|
+
if (p.approval && p.approval.status !== 'none') {
|
|
52
|
+
approvalBits.push(`approval: ${p.approval.status}`);
|
|
53
|
+
if (p.approval.waiting_on)
|
|
54
|
+
approvalBits.push(`waiting on: ${p.approval.waiting_on}`);
|
|
55
|
+
}
|
|
56
|
+
if (p.brand_id)
|
|
57
|
+
approvalBits.push(`brand: ${p.brand_id}`);
|
|
58
|
+
if (p.content_plan_id)
|
|
59
|
+
approvalBits.push(`plan: ${p.content_plan_id}`);
|
|
60
|
+
const approvalSuffix = approvalBits.length > 0 ? ` [${approvalBits.join(', ')}]` : '';
|
|
61
|
+
return `• [${p.status}] #${p.id}: ${caption} (${date})${approvalSuffix}`;
|
|
43
62
|
});
|
|
44
63
|
return [
|
|
45
64
|
`Posts (${posts.length} of ${total}):`,
|
|
46
65
|
...lines,
|
|
47
66
|
'',
|
|
48
|
-
`View in
|
|
49
|
-
'Next step for the AI agent: summarize the queue in plain language and use the dashboard link above if the user wants to inspect it in
|
|
67
|
+
`View in posterly: ${dashboardUrlForPostList(posts)}`,
|
|
68
|
+
'Next step for the AI agent: summarize the queue in plain language and use the dashboard link above if the user wants to inspect it in posterly.',
|
|
50
69
|
].join('\n');
|
|
51
70
|
},
|
|
52
71
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export const replyToCommentTool = {
|
|
3
3
|
name: 'reply_to_comment',
|
|
4
|
-
description: 'Reply to an Instagram, Facebook Page, or
|
|
4
|
+
description: 'Reply to an Instagram, Facebook Page, Threads, or LinkedIn Page inbox comment. WRITE: posts to the social network. Facebook has no private comment replies. Threads has no DMs, so only public replies. Instagram supports public or private. LinkedIn Page comments are public replies only. Requires a Pro plan or higher plus posts:write. Pass confirm=true only after explicit user confirmation.',
|
|
5
5
|
inputSchema: z.object({
|
|
6
6
|
comment_id: z.string().min(1).describe('Comment ID from list_comments.'),
|
|
7
7
|
content: z.string().min(1).describe('Reply text to post.'),
|
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
const DEFAULT_RETURN_PATH = '/dashboard/api?api_signup=success';
|
|
3
3
|
export const startSignupTool = {
|
|
4
4
|
name: 'start_signup',
|
|
5
|
-
description: 'Start the public
|
|
5
|
+
description: 'Start the public posterly paid signup flow for a user before posterly API access exists. Returns a signup poll URL, a browser Checkout fallback URL, and, when enabled, a prepaid PaymentIntent the agent can pay with a Link Agent Wallet virtual card (credential_type: card). The user still sets a password and approves social OAuth in the browser. Never ask for card numbers, a posterly password, or social passwords in chat.',
|
|
6
6
|
inputSchema: z.object({
|
|
7
7
|
email: z
|
|
8
8
|
.string()
|
|
@@ -11,11 +11,11 @@ export const startSignupTool = {
|
|
|
11
11
|
name: z
|
|
12
12
|
.string()
|
|
13
13
|
.optional()
|
|
14
|
-
.describe('Optional user or workspace name to pass into
|
|
14
|
+
.describe('Optional user or workspace name to pass into posterly signup.'),
|
|
15
15
|
tier: z
|
|
16
16
|
.enum(['starter', 'pro', 'power_user', 'agency'])
|
|
17
17
|
.default('starter')
|
|
18
|
-
.describe('
|
|
18
|
+
.describe('posterly plan to start. Use starter unless the user explicitly chooses another plan.'),
|
|
19
19
|
interval: z
|
|
20
20
|
.enum(['month'])
|
|
21
21
|
.default('month')
|
|
@@ -27,7 +27,7 @@ export const startSignupTool = {
|
|
|
27
27
|
return_path: z
|
|
28
28
|
.string()
|
|
29
29
|
.default(DEFAULT_RETURN_PATH)
|
|
30
|
-
.describe('Where
|
|
30
|
+
.describe('Where posterly should continue after checkout and password setup.'),
|
|
31
31
|
source: z
|
|
32
32
|
.string()
|
|
33
33
|
.default('posterly_mcp_pre_auth_signup')
|
|
@@ -72,7 +72,7 @@ function formatSignupStart(response, includeRaw) {
|
|
|
72
72
|
const agentPay = response.agent_pay || session?.agent_pay;
|
|
73
73
|
const payment = response.payment || session?.payment;
|
|
74
74
|
const lines = [
|
|
75
|
-
'
|
|
75
|
+
'posterly signup started.',
|
|
76
76
|
sessionId ? `Signup session: ${sessionId}` : '',
|
|
77
77
|
`Status: ${status}`,
|
|
78
78
|
message ? `Message: ${message}` : '',
|
|
@@ -109,17 +109,17 @@ function formatSignupStart(response, includeRaw) {
|
|
|
109
109
|
'1. Prefer agent_pay: confirm the PaymentIntent with a one-time Link Agent Wallet virtual card (credential_type: card). Never paste card numbers into chat or send them to posterly.',
|
|
110
110
|
'2. If the user would rather pay on the website, send them the Checkout handoff URL instead.',
|
|
111
111
|
'3. Call get_signup_session with the signup session ID until the status changes.',
|
|
112
|
-
'4. Tell the user when
|
|
113
|
-
'5. If status becomes agent_access_required, ask the user to install or paste the
|
|
114
|
-
'6. Do not ask for card details, the
|
|
112
|
+
'4. Tell the user when posterly sends the password setup email. Password, social OAuth, and publish approval stay in the browser.',
|
|
113
|
+
'5. If status becomes agent_access_required, ask the user to install or paste the posterly MCP/API setup instructions into this trusted AI chat, then onboard them in this conversation (about you, connect, brand voice, first post). Do not send them to /onboarding-v2.',
|
|
114
|
+
'6. Do not ask for card details, the posterly password, social passwords, or OAuth codes.',
|
|
115
115
|
]
|
|
116
116
|
: [
|
|
117
117
|
'1. If you hold an approved payment credential such as a Link agent wallet, you can pay the Checkout handoff URL yourself: create a spend request for the amount and merchant above, get the user\'s approval in the wallet, then complete the Checkout page with that credential. Never paste card numbers into chat or send them to posterly.',
|
|
118
118
|
'2. Otherwise, send the Checkout handoff URL to the user so they can pay securely in their browser. Use the raw Stripe checkout URL only if you preserve the full URL including any fragment. posterly does not email this link, so do not tell the user to check their inbox at this stage.',
|
|
119
119
|
'3. Call get_signup_session with the signup session ID until the status changes.',
|
|
120
|
-
'4. Tell the user when
|
|
121
|
-
'5. If status becomes agent_access_required, ask the user to install or paste the
|
|
122
|
-
'6. Do not ask for card details, the
|
|
120
|
+
'4. Tell the user when posterly sends the password setup email.',
|
|
121
|
+
'5. If status becomes agent_access_required, ask the user to install or paste the posterly MCP/API setup instructions into this trusted AI chat, then onboard them in this conversation (about you, connect, brand voice, first post). Do not send them to /onboarding-v2.',
|
|
122
|
+
'6. Do not ask for card details, the posterly password, social passwords, or OAuth codes.',
|
|
123
123
|
]),
|
|
124
124
|
includeRaw ? `\nRaw signup response:\n${JSON.stringify(response, null, 2)}` : '',
|
|
125
125
|
];
|
|
@@ -27,8 +27,8 @@ export declare const submitAgentFeedbackInputSchema: z.ZodObject<{
|
|
|
27
27
|
source: "mcp" | "cli" | "rest" | "skill";
|
|
28
28
|
tool: string;
|
|
29
29
|
outcome: "success" | "error" | "abandoned";
|
|
30
|
-
error_code?: string | undefined;
|
|
31
30
|
client?: string | undefined;
|
|
31
|
+
error_code?: string | undefined;
|
|
32
32
|
comment?: string | undefined;
|
|
33
33
|
context?: {
|
|
34
34
|
request_id?: string | undefined;
|
|
@@ -40,8 +40,8 @@ export declare const submitAgentFeedbackInputSchema: z.ZodObject<{
|
|
|
40
40
|
source: "mcp" | "cli" | "rest" | "skill";
|
|
41
41
|
tool: string;
|
|
42
42
|
outcome: "success" | "error" | "abandoned";
|
|
43
|
-
error_code?: string | undefined;
|
|
44
43
|
client?: string | undefined;
|
|
44
|
+
error_code?: string | undefined;
|
|
45
45
|
comment?: string | undefined;
|
|
46
46
|
context?: {
|
|
47
47
|
request_id?: string | undefined;
|
|
@@ -81,8 +81,8 @@ export declare const submitAgentFeedbackTool: {
|
|
|
81
81
|
source: "mcp" | "cli" | "rest" | "skill";
|
|
82
82
|
tool: string;
|
|
83
83
|
outcome: "success" | "error" | "abandoned";
|
|
84
|
-
error_code?: string | undefined;
|
|
85
84
|
client?: string | undefined;
|
|
85
|
+
error_code?: string | undefined;
|
|
86
86
|
comment?: string | undefined;
|
|
87
87
|
context?: {
|
|
88
88
|
request_id?: string | undefined;
|
|
@@ -94,8 +94,8 @@ export declare const submitAgentFeedbackTool: {
|
|
|
94
94
|
source: "mcp" | "cli" | "rest" | "skill";
|
|
95
95
|
tool: string;
|
|
96
96
|
outcome: "success" | "error" | "abandoned";
|
|
97
|
-
error_code?: string | undefined;
|
|
98
97
|
client?: string | undefined;
|
|
98
|
+
error_code?: string | undefined;
|
|
99
99
|
comment?: string | undefined;
|
|
100
100
|
context?: {
|
|
101
101
|
request_id?: string | undefined;
|
|
@@ -21,9 +21,9 @@ export declare const submitProductFeedbackInputSchema: z.ZodObject<{
|
|
|
21
21
|
title: string;
|
|
22
22
|
confirm: true;
|
|
23
23
|
category: "bug" | "idea" | "feedback";
|
|
24
|
+
client?: string | undefined;
|
|
24
25
|
description?: string | undefined;
|
|
25
26
|
source?: "mcp" | "cli" | "rest" | "skill" | undefined;
|
|
26
|
-
client?: string | undefined;
|
|
27
27
|
context?: {
|
|
28
28
|
request_id?: string | undefined;
|
|
29
29
|
related_tool?: string | undefined;
|
|
@@ -32,9 +32,9 @@ export declare const submitProductFeedbackInputSchema: z.ZodObject<{
|
|
|
32
32
|
title: string;
|
|
33
33
|
confirm: true;
|
|
34
34
|
category: "bug" | "idea" | "feedback";
|
|
35
|
+
client?: string | undefined;
|
|
35
36
|
description?: string | undefined;
|
|
36
37
|
source?: "mcp" | "cli" | "rest" | "skill" | undefined;
|
|
37
|
-
client?: string | undefined;
|
|
38
38
|
context?: {
|
|
39
39
|
request_id?: string | undefined;
|
|
40
40
|
related_tool?: string | undefined;
|
|
@@ -65,9 +65,9 @@ export declare const submitProductFeedbackTool: {
|
|
|
65
65
|
title: string;
|
|
66
66
|
confirm: true;
|
|
67
67
|
category: "bug" | "idea" | "feedback";
|
|
68
|
+
client?: string | undefined;
|
|
68
69
|
description?: string | undefined;
|
|
69
70
|
source?: "mcp" | "cli" | "rest" | "skill" | undefined;
|
|
70
|
-
client?: string | undefined;
|
|
71
71
|
context?: {
|
|
72
72
|
request_id?: string | undefined;
|
|
73
73
|
related_tool?: string | undefined;
|
|
@@ -76,9 +76,9 @@ export declare const submitProductFeedbackTool: {
|
|
|
76
76
|
title: string;
|
|
77
77
|
confirm: true;
|
|
78
78
|
category: "bug" | "idea" | "feedback";
|
|
79
|
+
client?: string | undefined;
|
|
79
80
|
description?: string | undefined;
|
|
80
81
|
source?: "mcp" | "cli" | "rest" | "skill" | undefined;
|
|
81
|
-
client?: string | undefined;
|
|
82
82
|
context?: {
|
|
83
83
|
request_id?: string | undefined;
|
|
84
84
|
related_tool?: string | undefined;
|
package/dist/tools/sync-inbox.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export const syncInboxTool = {
|
|
3
3
|
name: 'sync_inbox',
|
|
4
|
-
description: 'Pull the latest Instagram, Facebook Page, or
|
|
4
|
+
description: 'Pull the latest Instagram, Facebook Page, Threads, or LinkedIn inbox data for one connected account. Honors a 30-second cooldown (60 seconds for LinkedIn). Threads and LinkedIn sync comments only (no DMs). Requires a Pro plan or higher plus posts:write.',
|
|
5
5
|
inputSchema: z.object({
|
|
6
6
|
account_id: z.number().int().positive().describe('Social account ID from list_accounts.'),
|
|
7
|
-
sync_type: z.enum(['all', 'dm', 'comments']).optional().describe('What to sync. Defaults to all. Threads
|
|
7
|
+
sync_type: z.enum(['all', 'dm', 'comments']).optional().describe('What to sync. Defaults to all. Threads and LinkedIn are comments-only.'),
|
|
8
8
|
workspace_id: z.string().optional().describe('Workspace ID from whoami.'),
|
|
9
9
|
}),
|
|
10
10
|
async execute(client, input) {
|
|
@@ -116,8 +116,8 @@ export const updatePostTool = {
|
|
|
116
116
|
.string()
|
|
117
117
|
.optional()
|
|
118
118
|
.describe('New scheduled time (ISO 8601, e.g. 2026-03-10T09:00:00Z)'),
|
|
119
|
-
media_url: z.string().optional().describe('New media URL. Posterly storage URLs are used as-is; third-party HTTP(S) URLs are copied into
|
|
120
|
-
media_urls: z.array(z.string()).optional().describe('New media URLs for carousel. Posterly storage URLs are used as-is; third-party HTTP(S) URLs are copied into
|
|
119
|
+
media_url: z.string().optional().describe('New media URL. Posterly storage URLs are used as-is; third-party HTTP(S) URLs are copied into posterly storage before the post is saved. Short-lived signed URLs must still be live when this tool runs.'),
|
|
120
|
+
media_urls: z.array(z.string()).optional().describe('New media URLs for carousel. Posterly storage URLs are used as-is; third-party HTTP(S) URLs are copied into posterly storage before the post is saved.'),
|
|
121
121
|
post_type: z
|
|
122
122
|
.string()
|
|
123
123
|
.optional()
|
|
@@ -6,7 +6,7 @@ export declare const updateWebhookTool: {
|
|
|
6
6
|
inputSchema: z.ZodObject<{
|
|
7
7
|
webhook_id: z.ZodString;
|
|
8
8
|
url: z.ZodOptional<z.ZodString>;
|
|
9
|
-
events: z.ZodOptional<z.ZodArray<z.ZodEnum<["webhook.test", "post.created", "post.updated", "post.deleted", "post.publishing", "post.published", "post.failed", "account.disconnected", "analytics.synced"]>, "many">>;
|
|
9
|
+
events: z.ZodOptional<z.ZodArray<z.ZodEnum<["webhook.test", "post.created", "post.updated", "post.deleted", "post.publishing", "post.published", "post.failed", "account.disconnected", "analytics.synced", "approval.requested", "approval.approved", "approval.changes_requested", "approval.rejected", "approval.commented"]>, "many">>;
|
|
10
10
|
workspace_id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNull]>>;
|
|
11
11
|
description: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNull]>>;
|
|
12
12
|
is_active: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -17,7 +17,7 @@ export declare const updateWebhookTool: {
|
|
|
17
17
|
description?: string | null | undefined;
|
|
18
18
|
workspace_id?: string | null | undefined;
|
|
19
19
|
url?: string | undefined;
|
|
20
|
-
events?: ("webhook.test" | "post.created" | "post.updated" | "post.deleted" | "post.publishing" | "post.published" | "post.failed" | "account.disconnected" | "analytics.synced")[] | undefined;
|
|
20
|
+
events?: ("webhook.test" | "post.created" | "post.updated" | "post.deleted" | "post.publishing" | "post.published" | "post.failed" | "account.disconnected" | "analytics.synced" | "approval.requested" | "approval.approved" | "approval.changes_requested" | "approval.rejected" | "approval.commented")[] | undefined;
|
|
21
21
|
is_active?: boolean | undefined;
|
|
22
22
|
}, {
|
|
23
23
|
confirm: true;
|
|
@@ -25,7 +25,7 @@ export declare const updateWebhookTool: {
|
|
|
25
25
|
description?: string | null | undefined;
|
|
26
26
|
workspace_id?: string | null | undefined;
|
|
27
27
|
url?: string | undefined;
|
|
28
|
-
events?: ("webhook.test" | "post.created" | "post.updated" | "post.deleted" | "post.publishing" | "post.published" | "post.failed" | "account.disconnected" | "analytics.synced")[] | undefined;
|
|
28
|
+
events?: ("webhook.test" | "post.created" | "post.updated" | "post.deleted" | "post.publishing" | "post.published" | "post.failed" | "account.disconnected" | "analytics.synced" | "approval.requested" | "approval.approved" | "approval.changes_requested" | "approval.rejected" | "approval.commented")[] | undefined;
|
|
29
29
|
is_active?: boolean | undefined;
|
|
30
30
|
}>;
|
|
31
31
|
execute(client: PosterlyClient, input: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export const uploadMediaFromUrlTool = {
|
|
3
3
|
name: 'upload_media_from_url',
|
|
4
|
-
description: 'Fetch an image or video from a public HTTPS URL into
|
|
4
|
+
description: 'Fetch an image or video from a public HTTPS URL into posterly storage. Returns a URL that can be used with create_post. You can also pass public HTTP(S) media URLs directly to create_post/update_post; use this tool when you want to upload once and reuse the returned posterly URL.',
|
|
5
5
|
inputSchema: z.object({
|
|
6
6
|
url: z.string().url().describe('Public HTTPS URL for the image or video. Local/private network URLs are blocked.'),
|
|
7
7
|
filename: z.string().optional().describe('Optional filename override.'),
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
export declare const WEBHOOK_EVENTS: readonly ["webhook.test", "post.created", "post.updated", "post.deleted", "post.publishing", "post.published", "post.failed", "account.disconnected", "analytics.synced"];
|
|
3
|
-
export declare const webhookEventSchema: z.ZodEnum<["webhook.test", "post.created", "post.updated", "post.deleted", "post.publishing", "post.published", "post.failed", "account.disconnected", "analytics.synced"]>;
|
|
2
|
+
export declare const WEBHOOK_EVENTS: readonly ["webhook.test", "post.created", "post.updated", "post.deleted", "post.publishing", "post.published", "post.failed", "account.disconnected", "analytics.synced", "approval.requested", "approval.approved", "approval.changes_requested", "approval.rejected", "approval.commented"];
|
|
3
|
+
export declare const webhookEventSchema: z.ZodEnum<["webhook.test", "post.created", "post.updated", "post.deleted", "post.publishing", "post.published", "post.failed", "account.disconnected", "analytics.synced", "approval.requested", "approval.approved", "approval.changes_requested", "approval.rejected", "approval.commented"]>;
|
|
@@ -9,5 +9,10 @@ export const WEBHOOK_EVENTS = [
|
|
|
9
9
|
'post.failed',
|
|
10
10
|
'account.disconnected',
|
|
11
11
|
'analytics.synced',
|
|
12
|
+
'approval.requested',
|
|
13
|
+
'approval.approved',
|
|
14
|
+
'approval.changes_requested',
|
|
15
|
+
'approval.rejected',
|
|
16
|
+
'approval.commented',
|
|
12
17
|
];
|
|
13
18
|
export const webhookEventSchema = z.enum(WEBHOOK_EVENTS);
|
package/dist/tools/whoami.js
CHANGED
|
@@ -7,7 +7,7 @@ export const whoamiTool = {
|
|
|
7
7
|
async execute(client) {
|
|
8
8
|
const info = await client.whoami();
|
|
9
9
|
return [
|
|
10
|
-
mdTitle('
|
|
10
|
+
mdTitle('posterly identity'),
|
|
11
11
|
mdKeyValue([
|
|
12
12
|
['User', info.user.email || code(info.user.id)],
|
|
13
13
|
['API scopes', info.api_key.scopes.join(', ') || 'none'],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "posterly-mcp-server",
|
|
3
|
-
"version": "0.43.
|
|
3
|
+
"version": "0.43.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.43.
|
|
6
|
+
"version": "0.43.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.43.
|
|
17
|
+
"version": "0.43.5",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
|
20
20
|
},
|
package/src/lib/api-client.ts
CHANGED
|
@@ -138,6 +138,17 @@ export interface DismissSuggestionResponse {
|
|
|
138
138
|
status: string;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
export type ApiPostApprovalStatus = 'none' | 'pending_client' | 'approved' | 'changes_requested' | 'rejected';
|
|
142
|
+
|
|
143
|
+
export interface ApiPostApproval {
|
|
144
|
+
status: ApiPostApprovalStatus;
|
|
145
|
+
asset_status: ApiPostApprovalStatus;
|
|
146
|
+
caption_status: ApiPostApprovalStatus;
|
|
147
|
+
waiting_on: 'client' | 'team' | null;
|
|
148
|
+
requested_at: string | null;
|
|
149
|
+
completed_at: string | null;
|
|
150
|
+
}
|
|
151
|
+
|
|
141
152
|
export interface Post {
|
|
142
153
|
id: number;
|
|
143
154
|
content: string;
|
|
@@ -154,6 +165,9 @@ export interface Post {
|
|
|
154
165
|
reel_cover_url?: string | null;
|
|
155
166
|
reel_cover_method?: string | null;
|
|
156
167
|
reel_thumb_offset?: number | null;
|
|
168
|
+
brand_id?: string | null;
|
|
169
|
+
content_plan_id?: string | null;
|
|
170
|
+
approval?: ApiPostApproval;
|
|
157
171
|
}
|
|
158
172
|
|
|
159
173
|
export interface InstagramPostSettings {
|
|
@@ -1095,7 +1109,7 @@ export class PosterlyClient {
|
|
|
1095
1109
|
private requireApiKey(): void {
|
|
1096
1110
|
if (!this.apiKey) {
|
|
1097
1111
|
throw new Error(
|
|
1098
|
-
'
|
|
1112
|
+
'posterly access is not installed yet. Use start_signup to begin paid setup, or set POSTERLY_API_KEY after signup.'
|
|
1099
1113
|
);
|
|
1100
1114
|
}
|
|
1101
1115
|
}
|
|
@@ -1354,6 +1368,8 @@ export class PosterlyClient {
|
|
|
1354
1368
|
limit?: number;
|
|
1355
1369
|
offset?: number;
|
|
1356
1370
|
workspace_id?: string;
|
|
1371
|
+
approval_status?: string;
|
|
1372
|
+
brand_id?: string;
|
|
1357
1373
|
}): Promise<{ posts: Post[]; total: number }> {
|
|
1358
1374
|
const searchParams = new URLSearchParams();
|
|
1359
1375
|
if (params?.status) searchParams.set('status', params.status);
|
|
@@ -1362,6 +1378,8 @@ export class PosterlyClient {
|
|
|
1362
1378
|
if (params?.limit) searchParams.set('limit', String(params.limit));
|
|
1363
1379
|
if (params?.offset) searchParams.set('offset', String(params.offset));
|
|
1364
1380
|
if (params?.workspace_id) searchParams.set('workspace_id', params.workspace_id);
|
|
1381
|
+
if (params?.approval_status) searchParams.set('approval_status', params.approval_status);
|
|
1382
|
+
if (params?.brand_id) searchParams.set('brand_id', params.brand_id);
|
|
1365
1383
|
|
|
1366
1384
|
const qs = searchParams.toString();
|
|
1367
1385
|
return this.request('GET', `/posts${qs ? `?${qs}` : ''}`);
|
|
@@ -2137,7 +2155,7 @@ export function validatePosterlyBaseUrlForKeyUse(baseUrl: string): void {
|
|
|
2137
2155
|
!flagEnabled('POSTERLY_ALLOW_CUSTOM_BASE_URL')
|
|
2138
2156
|
) {
|
|
2139
2157
|
throw new Error(
|
|
2140
|
-
`Refusing to send an API key to custom host ${baseUrl}. Set POSTERLY_ALLOW_CUSTOM_BASE_URL=true only for a trusted
|
|
2158
|
+
`Refusing to send an API key to custom host ${baseUrl}. Set POSTERLY_ALLOW_CUSTOM_BASE_URL=true only for a trusted posterly deployment.`
|
|
2141
2159
|
);
|
|
2142
2160
|
}
|
|
2143
2161
|
}
|
package/src/lib/format.ts
CHANGED
|
@@ -103,7 +103,7 @@ export function mdSuccess(title: string, rows: Array<[string, CellValue]>, nextS
|
|
|
103
103
|
|
|
104
104
|
export function mdError(message: string): string {
|
|
105
105
|
return [
|
|
106
|
-
mdTitle('⚠️
|
|
106
|
+
mdTitle('⚠️ posterly tool error'),
|
|
107
107
|
`**Message:** ${message}`,
|
|
108
108
|
].join('\n');
|
|
109
109
|
}
|
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.43.
|
|
8
|
+
export const POSTERLY_MCP_VERSION = '0.43.5';
|
package/src/tools/ask-support.ts
CHANGED
|
@@ -20,7 +20,7 @@ function formatSupportAnswer(result: AskSupportResponse): string {
|
|
|
20
20
|
].filter(Boolean);
|
|
21
21
|
|
|
22
22
|
return [
|
|
23
|
-
mdTitle('
|
|
23
|
+
mdTitle('posterly Support'),
|
|
24
24
|
mdQuote(result.message?.content || 'No support answer returned.'),
|
|
25
25
|
sources.length ? mdSection('Sources', mdBullets(sources)) : '',
|
|
26
26
|
mdSection('Status', mdBullets(status)),
|
|
@@ -31,7 +31,7 @@ const supportInputSchema = z.object({
|
|
|
31
31
|
question: z.string().trim().min(1).max(4000),
|
|
32
32
|
workspace_id: z.string().optional().describe('Workspace to inspect. Omit to use the API-key scoped workspace or personal workspace.'),
|
|
33
33
|
conversation_id: z.string().optional().describe('Continue a previous support conversation.'),
|
|
34
|
-
page_url: z.string().max(2000).optional().describe('Optional
|
|
34
|
+
page_url: z.string().max(2000).optional().describe('Optional posterly page URL for context.'),
|
|
35
35
|
post_id: z.number().int().positive().optional().describe('Optional post ID to inspect directly.'),
|
|
36
36
|
referenced_post_ids: z.array(z.number().int().positive()).max(5).optional().describe('Optional post IDs to include in read-only diagnostics.'),
|
|
37
37
|
request_human: z.boolean().default(false).optional().describe('Ask for human review. Does not create a ticket unless confirm_escalation is also true.'),
|
|
@@ -49,7 +49,7 @@ async function executeSupport(client: PosterlyClient, input: AskSupportPayload)
|
|
|
49
49
|
export const askSupportTool = {
|
|
50
50
|
name: 'ask_support',
|
|
51
51
|
description:
|
|
52
|
-
'Ask
|
|
52
|
+
'Ask posterly Support AI an authenticated question using posterly docs plus read-only account/post diagnostics for the caller workspace. Requires POSTERLY_API_KEY with accounts:read and posts:read scopes. Human ticket creation requires request_human=true and confirm_escalation=true after explicit user confirmation.',
|
|
53
53
|
inputSchema: supportInputSchema,
|
|
54
54
|
execute: executeSupport,
|
|
55
55
|
};
|
|
@@ -25,7 +25,7 @@ function formatCreatedApiKey(result: CreateApiKeyResponse): string {
|
|
|
25
25
|
export const createApiKeyTool = {
|
|
26
26
|
name: 'create_api_key',
|
|
27
27
|
description:
|
|
28
|
-
'Create a new
|
|
28
|
+
'Create a new posterly API key for the authenticated user. SECRET-CREATING WRITE: only use after explicit user confirmation. The new key can only request scopes already present on the calling dashboard-created API key; OAuth and managed assistant tokens cannot mint keys.',
|
|
29
29
|
inputSchema: z.object({
|
|
30
30
|
name: z.string().trim().min(1).max(120).optional().describe('Human-readable key name.'),
|
|
31
31
|
scopes: z.array(scopeSchema).min(1).max(8).optional().describe('Scopes for the new key. Omit to copy the calling key scopes. Cannot exceed the calling key scopes. billing:read and billing:write allow managing the posterly subscription (cancel/pause/resume/downgrade).'),
|
|
@@ -6,7 +6,7 @@ import { CONNECT_INPUTS } from '../generated/platform-manifest.js';
|
|
|
6
6
|
export const createConnectSessionTool = {
|
|
7
7
|
name: 'create_connect_session',
|
|
8
8
|
description:
|
|
9
|
-
'Create a short-lived
|
|
9
|
+
'Create a short-lived posterly dashboard handoff session for connecting a social account. Send the secure connection URL to the user, then poll get_connect_session to narrate progress. Do not ask for social media passwords or OAuth codes; posterly handles those browser steps.',
|
|
10
10
|
inputSchema: z.object({
|
|
11
11
|
platform: z
|
|
12
12
|
.enum(CONNECT_INPUTS)
|
|
@@ -38,7 +38,7 @@ export const createConnectSessionTool = {
|
|
|
38
38
|
function formatConnectSession(session: ConnectSession, includeRaw: boolean): string {
|
|
39
39
|
const connected = session.connected_count ?? session.connected_accounts?.length ?? 0;
|
|
40
40
|
return [
|
|
41
|
-
`
|
|
41
|
+
`posterly connect session created: ${session.id}`,
|
|
42
42
|
`Platform: ${session.platform}`,
|
|
43
43
|
`Status: ${session.status}`,
|
|
44
44
|
`Message: ${session.status_message}`,
|
|
@@ -49,7 +49,7 @@ function formatConnectSession(session: ConnectSession, includeRaw: boolean): str
|
|
|
49
49
|
`Expires: ${formatLocalDateTime(session.expires_at)}`,
|
|
50
50
|
`Dashboard connect page: ${DASHBOARD_CONNECT_URL}`,
|
|
51
51
|
'',
|
|
52
|
-
'Next step for the AI agent: send the secure connection URL to the user, explain that
|
|
52
|
+
'Next step for the AI agent: send the secure connection URL to the user, explain that posterly will handle the browser OAuth step, then poll get_connect_session until the account is connected or the session ends.',
|
|
53
53
|
includeRaw ? `\nRaw connect session:\n${JSON.stringify(session, null, 2)}` : '',
|
|
54
54
|
].filter(Boolean).join('\n');
|
|
55
55
|
}
|