posterly-mcp-server 0.23.2 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/generated/platform-manifest.d.ts +82 -66
- package/dist/generated/platform-manifest.js +126 -77
- package/dist/index.js +40 -0
- package/dist/lib/api-client.d.ts +77 -0
- package/dist/lib/api-client.js +30 -0
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/tools/create-connect-session.d.ts +3 -3
- package/dist/tools/create-connect-session.js +1 -1
- package/dist/tools/create-post.d.ts +27 -9
- package/dist/tools/create-post.js +2 -1
- package/dist/tools/create-posts-batch.d.ts +12 -5
- package/dist/tools/dismiss-suggestion.d.ts +16 -0
- package/dist/tools/dismiss-suggestion.js +18 -0
- package/dist/tools/get-connect-link.d.ts +3 -3
- package/dist/tools/get-performance-profile.d.ts +16 -0
- package/dist/tools/get-performance-profile.js +39 -0
- package/dist/tools/get-platform-schema.d.ts +3 -3
- package/dist/tools/get-post-insights.d.ts +28 -0
- package/dist/tools/get-post-insights.js +39 -0
- package/dist/tools/get-video-job.d.ts +2 -2
- package/dist/tools/list-activity.d.ts +2 -2
- package/dist/tools/list-post-suggestions.d.ts +24 -0
- package/dist/tools/list-post-suggestions.js +36 -0
- package/dist/tools/list-posts.d.ts +3 -3
- package/dist/tools/trigger-platform-helper.d.ts +6 -6
- package/dist/tools/update-post.d.ts +8 -0
- package/dist/tools/update-post.js +2 -1
- package/package.json +1 -1
- package/src/generated/platform-manifest.ts +126 -77
- package/src/index.ts +60 -0
- package/src/lib/api-client.ts +107 -0
- package/src/lib/version.ts +1 -1
- package/src/tools/create-connect-session.ts +1 -1
- package/src/tools/create-post.ts +2 -1
- package/src/tools/dismiss-suggestion.ts +22 -0
- package/src/tools/get-performance-profile.ts +47 -0
- package/src/tools/get-post-insights.ts +60 -0
- package/src/tools/list-post-suggestions.ts +56 -0
- package/src/tools/update-post.ts +2 -1
package/src/index.ts
CHANGED
|
@@ -15,6 +15,10 @@ import { getBrandTool } from './tools/get-brand.js';
|
|
|
15
15
|
import { listBrandAccountsTool } from './tools/list-brand-accounts.js';
|
|
16
16
|
import { getBrandProfileTool } from './tools/get-brand-profile.js';
|
|
17
17
|
import { getLearnedVoiceTool } from './tools/get-learned-voice.js';
|
|
18
|
+
import { getPerformanceProfileTool } from './tools/get-performance-profile.js';
|
|
19
|
+
import { getPostInsightsTool } from './tools/get-post-insights.js';
|
|
20
|
+
import { listPostSuggestionsTool } from './tools/list-post-suggestions.js';
|
|
21
|
+
import { dismissSuggestionTool } from './tools/dismiss-suggestion.js';
|
|
18
22
|
import { createPostTool } from './tools/create-post.js';
|
|
19
23
|
import { createPostsBatchTool } from './tools/create-posts-batch.js';
|
|
20
24
|
import { findSlotTool } from './tools/find-slot.js';
|
|
@@ -412,6 +416,62 @@ server.tool(
|
|
|
412
416
|
}
|
|
413
417
|
);
|
|
414
418
|
|
|
419
|
+
server.tool(
|
|
420
|
+
getPerformanceProfileTool.name,
|
|
421
|
+
getPerformanceProfileTool.description,
|
|
422
|
+
getPerformanceProfileTool.inputSchema.shape,
|
|
423
|
+
async (input) => {
|
|
424
|
+
try {
|
|
425
|
+
const text = await getPerformanceProfileTool.execute(client, input as any);
|
|
426
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
427
|
+
} catch (err: any) {
|
|
428
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
server.tool(
|
|
434
|
+
getPostInsightsTool.name,
|
|
435
|
+
getPostInsightsTool.description,
|
|
436
|
+
getPostInsightsTool.inputSchema.shape,
|
|
437
|
+
async (input) => {
|
|
438
|
+
try {
|
|
439
|
+
const text = await getPostInsightsTool.execute(client, input as any);
|
|
440
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
441
|
+
} catch (err: any) {
|
|
442
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
server.tool(
|
|
448
|
+
listPostSuggestionsTool.name,
|
|
449
|
+
listPostSuggestionsTool.description,
|
|
450
|
+
listPostSuggestionsTool.inputSchema.shape,
|
|
451
|
+
async (input) => {
|
|
452
|
+
try {
|
|
453
|
+
const text = await listPostSuggestionsTool.execute(client, input as any);
|
|
454
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
455
|
+
} catch (err: any) {
|
|
456
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
server.tool(
|
|
462
|
+
dismissSuggestionTool.name,
|
|
463
|
+
dismissSuggestionTool.description,
|
|
464
|
+
dismissSuggestionTool.inputSchema.shape,
|
|
465
|
+
async (input) => {
|
|
466
|
+
try {
|
|
467
|
+
const text = await dismissSuggestionTool.execute(client, input as any);
|
|
468
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
469
|
+
} catch (err: any) {
|
|
470
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
);
|
|
474
|
+
|
|
415
475
|
server.tool(
|
|
416
476
|
createPostTool.name,
|
|
417
477
|
createPostTool.description,
|
package/src/lib/api-client.ts
CHANGED
|
@@ -62,6 +62,77 @@ export interface LearnedVoice {
|
|
|
62
62
|
derived_at: string | null;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
export interface PerformanceProfile {
|
|
66
|
+
social_account_id: number;
|
|
67
|
+
platform: string;
|
|
68
|
+
username: string | null;
|
|
69
|
+
sample_size: number;
|
|
70
|
+
window_days: number;
|
|
71
|
+
engagement_rate_avg: number | null;
|
|
72
|
+
engagement_rate_trend: 'up' | 'down' | 'flat' | 'unknown';
|
|
73
|
+
bullets: string[];
|
|
74
|
+
narrative_summary: string | null;
|
|
75
|
+
stats: Record<string, unknown>;
|
|
76
|
+
derived_at: string | null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type PerformanceProfileNullReason =
|
|
80
|
+
| 'insufficient_data'
|
|
81
|
+
| 'feature_not_available'
|
|
82
|
+
| 'platform_not_measurable';
|
|
83
|
+
|
|
84
|
+
export interface PerformanceProfileResponse {
|
|
85
|
+
performance_profile: PerformanceProfile | null;
|
|
86
|
+
reason: PerformanceProfileNullReason | null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface FeedbackInsight {
|
|
90
|
+
id: string;
|
|
91
|
+
post_id: number;
|
|
92
|
+
social_account_id: number;
|
|
93
|
+
username: string | null;
|
|
94
|
+
platform: string;
|
|
95
|
+
checkpoint: '1h' | '6h' | '24h' | '72h' | '7d';
|
|
96
|
+
performance_tier: string;
|
|
97
|
+
action_type: string;
|
|
98
|
+
title: string;
|
|
99
|
+
diagnosis: string;
|
|
100
|
+
next_action: string;
|
|
101
|
+
impact_score: number;
|
|
102
|
+
confidence: number;
|
|
103
|
+
metrics: Record<string, unknown>;
|
|
104
|
+
baseline: Record<string, unknown>;
|
|
105
|
+
caption: string | null;
|
|
106
|
+
permalink: string | null;
|
|
107
|
+
published_at: string | null;
|
|
108
|
+
created_at: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface FeedbackInsightsResponse {
|
|
112
|
+
insights: FeedbackInsight[];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface PostSuggestion {
|
|
116
|
+
id: string;
|
|
117
|
+
social_account_id: number;
|
|
118
|
+
platform: string;
|
|
119
|
+
account: { id: number; username: string | null; platform: string };
|
|
120
|
+
caption: string;
|
|
121
|
+
rationale: string | null;
|
|
122
|
+
period_key: string | null;
|
|
123
|
+
status: string;
|
|
124
|
+
created_at: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface PostSuggestionsResponse {
|
|
128
|
+
suggestions: PostSuggestion[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface DismissSuggestionResponse {
|
|
132
|
+
ok: boolean;
|
|
133
|
+
status: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
65
136
|
export interface Post {
|
|
66
137
|
id: number;
|
|
67
138
|
content: string;
|
|
@@ -889,6 +960,42 @@ export class PosterlyClient {
|
|
|
889
960
|
return this.request('GET', `/accounts/${encodeURIComponent(String(id))}/learned-voice`);
|
|
890
961
|
}
|
|
891
962
|
|
|
963
|
+
async getAccountPerformanceProfile(id: string | number): Promise<PerformanceProfileResponse> {
|
|
964
|
+
return this.request('GET', `/accounts/${encodeURIComponent(String(id))}/performance-profile`);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
async getPostInsights(params?: {
|
|
968
|
+
account_id?: string | number;
|
|
969
|
+
post_id?: number;
|
|
970
|
+
checkpoint?: '1h' | '6h' | '24h' | '72h' | '7d';
|
|
971
|
+
limit?: number;
|
|
972
|
+
}): Promise<FeedbackInsightsResponse> {
|
|
973
|
+
const searchParams = new URLSearchParams();
|
|
974
|
+
if (params?.account_id) searchParams.set('account_id', String(params.account_id));
|
|
975
|
+
if (params?.post_id) searchParams.set('post_id', String(params.post_id));
|
|
976
|
+
if (params?.checkpoint) searchParams.set('checkpoint', params.checkpoint);
|
|
977
|
+
if (params?.limit) searchParams.set('limit', String(params.limit));
|
|
978
|
+
const qs = searchParams.toString();
|
|
979
|
+
return this.request('GET', `/analytics/insights${qs ? `?${qs}` : ''}`);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
async listPostSuggestions(params?: {
|
|
983
|
+
account_id?: string | number;
|
|
984
|
+
status?: 'pending' | 'scheduled' | 'dismissed';
|
|
985
|
+
limit?: number;
|
|
986
|
+
}): Promise<PostSuggestionsResponse> {
|
|
987
|
+
const searchParams = new URLSearchParams();
|
|
988
|
+
if (params?.account_id) searchParams.set('account_id', String(params.account_id));
|
|
989
|
+
if (params?.status) searchParams.set('status', params.status);
|
|
990
|
+
if (params?.limit) searchParams.set('limit', String(params.limit));
|
|
991
|
+
const qs = searchParams.toString();
|
|
992
|
+
return this.request('GET', `/suggestions${qs ? `?${qs}` : ''}`);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
async dismissSuggestion(id: string): Promise<DismissSuggestionResponse> {
|
|
996
|
+
return this.request('POST', `/suggestions/${encodeURIComponent(id)}/dismiss`, {});
|
|
997
|
+
}
|
|
998
|
+
|
|
892
999
|
async listPosts(params?: {
|
|
893
1000
|
status?: string;
|
|
894
1001
|
platform?: string;
|
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.24.1';
|
|
@@ -10,7 +10,7 @@ export const createConnectSessionTool = {
|
|
|
10
10
|
inputSchema: z.object({
|
|
11
11
|
platform: z
|
|
12
12
|
.enum(CONNECT_INPUTS)
|
|
13
|
-
.describe('Connection target such as instagram, meta, linkedin_page, twitter, google_business, telegram, or
|
|
13
|
+
.describe('Connection target such as instagram, meta, linkedin_page, twitter, google_business, telegram, bluesky, discord, mastodon, devto, hashnode, wordpress, or lemmy.'),
|
|
14
14
|
workspace_id: z
|
|
15
15
|
.string()
|
|
16
16
|
.optional()
|
package/src/tools/create-post.ts
CHANGED
|
@@ -39,6 +39,7 @@ export const platformSettingsSchema = z.object({
|
|
|
39
39
|
// Threads
|
|
40
40
|
reply_control: z.enum(['everyone', 'accounts_you_follow', 'mentioned_only', 'followers_only', 'parent_post_author_only']).optional(),
|
|
41
41
|
topic_tag: z.string().optional(), // Threads single discovery topic tag
|
|
42
|
+
share_to_instagram: z.boolean().optional(), // Threads: also share the published post to the linked Instagram account as a Story
|
|
42
43
|
text_attachment: z.record(z.unknown()).optional(),
|
|
43
44
|
// Facebook
|
|
44
45
|
text_format_preset_id: z.string().optional(),
|
|
@@ -109,7 +110,7 @@ export const createPostPayloadInputSchema = z.object({
|
|
|
109
110
|
post_type: z
|
|
110
111
|
.string()
|
|
111
112
|
.optional()
|
|
112
|
-
.describe('
|
|
113
|
+
.describe('Optional post type: text, image, video, carousel, reel, story, story_series, document, photo, cover_photo, x_thread, or threads_thread. Omit it for auto-detection from media. For TikTok photo posts, use photo/image/carousel or omit it; for Facebook cover photos, use cover_photo or platform_settings.post_type.'),
|
|
113
114
|
thread_posts: z
|
|
114
115
|
.array(z.string())
|
|
115
116
|
.optional()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
import { mdSuccess } from '../lib/format.js';
|
|
4
|
+
|
|
5
|
+
export const dismissSuggestionTool = {
|
|
6
|
+
name: 'dismiss_suggestion',
|
|
7
|
+
description:
|
|
8
|
+
'Dismiss a proactive post suggestion so it stops appearing. WRITE: confirm the exact suggestion with the user first. Never dismisses a suggestion that was already turned into a scheduled post (that would break adoption tracking).',
|
|
9
|
+
inputSchema: z.object({
|
|
10
|
+
suggestion_id: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe('Suggestion ID from list_post_suggestions.'),
|
|
13
|
+
}),
|
|
14
|
+
|
|
15
|
+
async execute(client: PosterlyClient, input: { suggestion_id: string }) {
|
|
16
|
+
const result = await client.dismissSuggestion(input.suggestion_id);
|
|
17
|
+
return mdSuccess('Suggestion dismissed', [
|
|
18
|
+
['Suggestion ID', input.suggestion_id],
|
|
19
|
+
['Status', result.status || 'dismissed'],
|
|
20
|
+
]);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
import { mdTitle, mdSection, mdKeyValue, mdBullets, mdQuote, mdEmpty, formatPercent, formatDateTime } from '../lib/format.js';
|
|
4
|
+
|
|
5
|
+
export const getPerformanceProfileTool = {
|
|
6
|
+
name: 'get_performance_profile',
|
|
7
|
+
description:
|
|
8
|
+
"Get the performance profile posterly has derived for a connected account from the last 90 days of per-post analytics. Returns coaching stats (format, timing, caption-length patterns), an engagement-rate trend, and a narrative summary. Keyed by social account ID (from list_accounts or list_brand_accounts). Returns nothing until the account has a derived profile; Google Business Profile and other platforms without per-post analytics never get one. Read-only; requires a Pro plan or higher.",
|
|
9
|
+
inputSchema: z.object({
|
|
10
|
+
account_id: z
|
|
11
|
+
.union([z.string(), z.number()])
|
|
12
|
+
.describe('The social account ID to inspect (from list_accounts or list_brand_accounts).'),
|
|
13
|
+
}),
|
|
14
|
+
|
|
15
|
+
async execute(client: PosterlyClient, input: { account_id: string | number }) {
|
|
16
|
+
const { performance_profile, reason } = await client.getAccountPerformanceProfile(input.account_id);
|
|
17
|
+
|
|
18
|
+
if (!performance_profile) {
|
|
19
|
+
const detail = reason === 'platform_not_measurable'
|
|
20
|
+
? 'This platform does not produce per-post analytics, so posterly cannot build a performance profile for it.'
|
|
21
|
+
: reason === 'feature_not_available'
|
|
22
|
+
? 'Performance profiles require a Pro plan or higher.'
|
|
23
|
+
: 'posterly has not built a performance profile for this account yet. It needs about 90 days of tracked posts to learn from.';
|
|
24
|
+
return mdEmpty('performance profile yet', detail);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const label = performance_profile.username
|
|
28
|
+
? `@${performance_profile.username}${performance_profile.platform ? ` (${performance_profile.platform})` : ''}`
|
|
29
|
+
: `account ${performance_profile.social_account_id}`;
|
|
30
|
+
|
|
31
|
+
const rate = performance_profile.engagement_rate_avg != null
|
|
32
|
+
? formatPercent(performance_profile.engagement_rate_avg * 100)
|
|
33
|
+
: 'n/a';
|
|
34
|
+
|
|
35
|
+
return [
|
|
36
|
+
mdTitle('Account performance profile', `${label} · last ${performance_profile.window_days ?? 90} days`),
|
|
37
|
+
performance_profile.narrative_summary ? mdSection('Summary', mdQuote(performance_profile.narrative_summary)) : '',
|
|
38
|
+
performance_profile.bullets.length ? mdSection('What is working', mdBullets(performance_profile.bullets)) : '',
|
|
39
|
+
mdSection('Stats', mdKeyValue([
|
|
40
|
+
['Posts sampled', performance_profile.sample_size],
|
|
41
|
+
['Avg engagement rate', rate],
|
|
42
|
+
['Engagement trend', performance_profile.engagement_rate_trend],
|
|
43
|
+
['Derived at', formatDateTime(performance_profile.derived_at)],
|
|
44
|
+
])),
|
|
45
|
+
].filter(Boolean).join('\n\n');
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
import { mdTitle, mdSection, mdKeyValue, mdEmpty, truncateText, formatDateTime } from '../lib/format.js';
|
|
4
|
+
|
|
5
|
+
export const getPostInsightsTool = {
|
|
6
|
+
name: 'get_post_insights',
|
|
7
|
+
description:
|
|
8
|
+
'List per-post feedback insights from the posterly performance feedback loop: for recently published posts, the performance tier (great/good/mixed/poor), a diagnosis, a suggested next action, metrics, and the baseline it was compared against. Filter by account_id, post_id, and checkpoint (1h, 6h, 24h, 72h, 7d). Read-only; requires a Pro plan or higher.',
|
|
9
|
+
inputSchema: z.object({
|
|
10
|
+
account_id: z
|
|
11
|
+
.union([z.string(), z.number()])
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('Filter to one social account ID (from list_accounts).'),
|
|
14
|
+
post_id: z.number().int().positive().optional().describe('Filter to one post ID.'),
|
|
15
|
+
checkpoint: z
|
|
16
|
+
.enum(['1h', '6h', '24h', '72h', '7d'])
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Filter to one checkpoint after publish.'),
|
|
19
|
+
limit: z.number().int().min(1).max(100).optional().describe('Max insights to return (default 20).'),
|
|
20
|
+
}),
|
|
21
|
+
|
|
22
|
+
async execute(
|
|
23
|
+
client: PosterlyClient,
|
|
24
|
+
input: {
|
|
25
|
+
account_id?: string | number;
|
|
26
|
+
post_id?: number;
|
|
27
|
+
checkpoint?: '1h' | '6h' | '24h' | '72h' | '7d';
|
|
28
|
+
limit?: number;
|
|
29
|
+
},
|
|
30
|
+
) {
|
|
31
|
+
const { insights } = await client.getPostInsights(input);
|
|
32
|
+
|
|
33
|
+
if (!insights.length) {
|
|
34
|
+
return mdEmpty(
|
|
35
|
+
'feedback insights',
|
|
36
|
+
'No per-post feedback insights are available for these filters yet. Insights appear as posts hit their 1h, 6h, 24h, 72h, and 7d checkpoints.',
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const blocks = insights.map((insight) => {
|
|
41
|
+
const heading = `${insight.title || 'Insight'} (${insight.performance_tier}, ${insight.checkpoint})`;
|
|
42
|
+
return mdSection(
|
|
43
|
+
heading,
|
|
44
|
+
mdKeyValue([
|
|
45
|
+
['Post ID', insight.post_id],
|
|
46
|
+
['Platform', insight.platform],
|
|
47
|
+
['Account', insight.username ? `@${insight.username}` : String(insight.social_account_id)],
|
|
48
|
+
['Diagnosis', truncateText(insight.diagnosis, 220)],
|
|
49
|
+
['Next action', truncateText(insight.next_action, 220)],
|
|
50
|
+
['Impact score', insight.impact_score],
|
|
51
|
+
['Confidence', insight.confidence],
|
|
52
|
+
['Published', formatDateTime(insight.published_at)],
|
|
53
|
+
['Permalink', insight.permalink || undefined],
|
|
54
|
+
]),
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return [mdTitle(`Post feedback insights (${insights.length})`), ...blocks].join('\n\n');
|
|
59
|
+
},
|
|
60
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
import { mdTitle, mdSection, mdKeyValue, mdEmpty, truncateText } from '../lib/format.js';
|
|
4
|
+
|
|
5
|
+
export const listPostSuggestionsTool = {
|
|
6
|
+
name: 'list_post_suggestions',
|
|
7
|
+
description:
|
|
8
|
+
"List proactive post suggestions: evidence-based weekly drafts posterly writes in each account's learned voice, each with a rationale tying it back to what has performed well for that account. Filter by account_id and status (pending, scheduled, dismissed; default pending). Read-only; requires a Pro plan or higher. Use dismiss_suggestion to hide one.",
|
|
9
|
+
inputSchema: z.object({
|
|
10
|
+
account_id: z
|
|
11
|
+
.union([z.string(), z.number()])
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('Filter to one social account ID (from list_accounts).'),
|
|
14
|
+
status: z
|
|
15
|
+
.enum(['pending', 'scheduled', 'dismissed'])
|
|
16
|
+
.optional()
|
|
17
|
+
.describe('Suggestion status to list (default pending).'),
|
|
18
|
+
limit: z.number().int().min(1).max(100).optional().describe('Max suggestions to return (default 20).'),
|
|
19
|
+
}),
|
|
20
|
+
|
|
21
|
+
async execute(
|
|
22
|
+
client: PosterlyClient,
|
|
23
|
+
input: {
|
|
24
|
+
account_id?: string | number;
|
|
25
|
+
status?: 'pending' | 'scheduled' | 'dismissed';
|
|
26
|
+
limit?: number;
|
|
27
|
+
},
|
|
28
|
+
) {
|
|
29
|
+
const { suggestions } = await client.listPostSuggestions(input);
|
|
30
|
+
|
|
31
|
+
if (!suggestions.length) {
|
|
32
|
+
return mdEmpty(
|
|
33
|
+
'post suggestions',
|
|
34
|
+
'No proactive post suggestions match these filters. posterly generates fresh drafts weekly for accounts with a learned voice.',
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const blocks = suggestions.map((suggestion) => {
|
|
39
|
+
const label = suggestion.account?.username
|
|
40
|
+
? `@${suggestion.account.username} (${suggestion.platform || suggestion.account?.platform})`
|
|
41
|
+
: `account ${suggestion.social_account_id}`;
|
|
42
|
+
return mdSection(
|
|
43
|
+
label,
|
|
44
|
+
mdKeyValue([
|
|
45
|
+
['Suggestion ID', suggestion.id],
|
|
46
|
+
['Status', suggestion.status],
|
|
47
|
+
['Period', suggestion.period_key || undefined],
|
|
48
|
+
['Caption', truncateText(suggestion.caption, 240)],
|
|
49
|
+
['Rationale', suggestion.rationale ? truncateText(suggestion.rationale, 240) : undefined],
|
|
50
|
+
]),
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return [mdTitle(`Post suggestions (${suggestions.length})`), ...blocks].join('\n\n');
|
|
55
|
+
},
|
|
56
|
+
};
|
package/src/tools/update-post.ts
CHANGED
|
@@ -30,6 +30,7 @@ const platformSettingsSchema = z.object({
|
|
|
30
30
|
poll: z.object({ options: z.array(z.string()), duration_minutes: z.number().positive().optional(), question: z.string().optional(), duration: z.enum(['ONE_DAY', 'THREE_DAYS', 'SEVEN_DAYS', 'FOURTEEN_DAYS']).optional(), allows_multiple_answers: z.boolean().optional() }).optional(),
|
|
31
31
|
reply_control: z.enum(['everyone', 'accounts_you_follow', 'mentioned_only', 'followers_only', 'parent_post_author_only']).optional(),
|
|
32
32
|
topic_tag: z.string().optional(),
|
|
33
|
+
share_to_instagram: z.boolean().optional(), // Threads: also share the published post to the linked Instagram account as a Story
|
|
33
34
|
text_attachment: z.record(z.unknown()).optional(),
|
|
34
35
|
text_format_preset_id: z.string().optional(),
|
|
35
36
|
document_title: z.string().optional(),
|
|
@@ -85,7 +86,7 @@ export const updatePostTool = {
|
|
|
85
86
|
post_type: z
|
|
86
87
|
.string()
|
|
87
88
|
.optional()
|
|
88
|
-
.describe('New post type: text, image, video, carousel, reel, story'),
|
|
89
|
+
.describe('New post type: text, image, video, carousel, reel, story, story_series, document, photo, or cover_photo'),
|
|
89
90
|
instagram_settings: instagramSettingsSchema
|
|
90
91
|
.optional()
|
|
91
92
|
.describe('Instagram-specific updates: post_type, collaborators, user_tags, first_comment, media_alt_texts, is_trial_reel, graduation_strategy (defaults to MANUAL), reel_cover_url, reel_thumb_offset.'),
|