posterly-mcp-server 0.19.1 → 0.19.2
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 +3 -1
- package/dist/index.js +50 -49
- package/dist/lib/api-client.js +1 -1
- package/dist/lib/format.d.ts +21 -0
- package/dist/lib/format.js +125 -0
- package/dist/tools/audit-google-business-profile.js +15 -12
- package/dist/tools/create-oauth-client.js +7 -1
- package/dist/tools/create-post.js +20 -27
- package/dist/tools/create-posts-batch.js +23 -15
- package/dist/tools/create-signed-upload.js +11 -8
- package/dist/tools/create-webhook.js +7 -2
- package/dist/tools/delete-google-business-review-reply.d.ts +1 -1
- package/dist/tools/delete-google-business-review-reply.js +4 -1
- package/dist/tools/delete-oauth-client.js +4 -1
- package/dist/tools/delete-post-group.js +5 -1
- package/dist/tools/delete-post.js +4 -1
- package/dist/tools/delete-webhook.js +4 -1
- package/dist/tools/disconnect-account.js +7 -5
- package/dist/tools/find-slot.js +10 -3
- package/dist/tools/generate-image.js +17 -19
- package/dist/tools/generate-video.js +11 -9
- package/dist/tools/get-account-analytics.js +64 -45
- package/dist/tools/get-brand-profile.js +34 -40
- package/dist/tools/get-brand.js +14 -16
- package/dist/tools/get-connect-link.js +26 -7
- package/dist/tools/get-google-business-review-link.js +6 -6
- package/dist/tools/get-platform-schema.js +23 -16
- package/dist/tools/get-post-analytics.js +53 -50
- package/dist/tools/get-post-missing.js +5 -1
- package/dist/tools/get-post.js +16 -14
- package/dist/tools/get-video-job.d.ts +2 -2
- package/dist/tools/get-video-job.js +28 -11
- package/dist/tools/get-video-options.js +21 -15
- package/dist/tools/get-x-posting-quota.js +12 -9
- package/dist/tools/list-accounts.js +12 -4
- package/dist/tools/list-activity.js +13 -18
- package/dist/tools/list-brand-accounts.js +11 -3
- package/dist/tools/list-brands.js +12 -11
- package/dist/tools/list-google-business-reviews.js +13 -11
- package/dist/tools/list-oauth-clients.js +10 -4
- package/dist/tools/list-platforms.js +12 -5
- package/dist/tools/list-posts.js +12 -7
- package/dist/tools/list-webhooks.js +13 -11
- package/dist/tools/reply-google-business-review.d.ts +1 -1
- package/dist/tools/reply-google-business-review.js +4 -1
- package/dist/tools/run-video-function.js +4 -4
- package/dist/tools/suggest-google-business-review-reply.js +5 -4
- package/dist/tools/test-webhook.js +7 -1
- package/dist/tools/trigger-platform-helper.js +5 -1
- package/dist/tools/update-oauth-client.js +8 -1
- package/dist/tools/update-post-release-id.js +5 -1
- package/dist/tools/update-post-status.d.ts +2 -2
- package/dist/tools/update-post-status.js +7 -1
- package/dist/tools/update-post.js +10 -4
- package/dist/tools/update-webhook.js +7 -1
- package/dist/tools/upload-media-from-url.js +7 -9
- package/dist/tools/upload-media.js +6 -2
- package/dist/tools/whoami.js +18 -17
- package/package.json +1 -1
- package/src/index.ts +50 -49
- package/src/lib/api-client.ts +1 -1
- package/src/lib/format.ts +132 -0
- package/src/tools/audit-google-business-profile.ts +18 -12
- package/src/tools/create-oauth-client.ts +8 -2
- package/src/tools/create-post.ts +20 -28
- package/src/tools/create-posts-batch.ts +23 -18
- package/src/tools/create-signed-upload.ts +11 -8
- package/src/tools/create-webhook.ts +7 -3
- package/src/tools/delete-google-business-review-reply.ts +4 -1
- package/src/tools/delete-oauth-client.ts +4 -1
- package/src/tools/delete-post-group.ts +5 -1
- package/src/tools/delete-post.ts +4 -1
- package/src/tools/delete-webhook.ts +4 -2
- package/src/tools/disconnect-account.ts +7 -5
- package/src/tools/find-slot.ts +13 -5
- package/src/tools/generate-image.ts +20 -20
- package/src/tools/generate-video.ts +11 -9
- package/src/tools/get-account-analytics.ts +70 -49
- package/src/tools/get-brand-profile.ts +34 -38
- package/src/tools/get-brand.ts +14 -14
- package/src/tools/get-connect-link.ts +29 -7
- package/src/tools/get-google-business-review-link.ts +6 -6
- package/src/tools/get-platform-schema.ts +29 -16
- package/src/tools/get-post-analytics.ts +58 -51
- package/src/tools/get-post-missing.ts +5 -2
- package/src/tools/get-post.ts +16 -13
- package/src/tools/get-video-job.ts +31 -11
- package/src/tools/get-video-options.ts +27 -15
- package/src/tools/get-x-posting-quota.ts +12 -9
- package/src/tools/list-accounts.ts +15 -6
- package/src/tools/list-activity.ts +16 -20
- package/src/tools/list-brand-accounts.ts +14 -6
- package/src/tools/list-brands.ts +16 -13
- package/src/tools/list-google-business-reviews.ts +16 -12
- package/src/tools/list-oauth-clients.ts +13 -4
- package/src/tools/list-platforms.ts +15 -5
- package/src/tools/list-posts.ts +15 -9
- package/src/tools/list-webhooks.ts +16 -12
- package/src/tools/reply-google-business-review.ts +4 -1
- package/src/tools/run-video-function.ts +4 -4
- package/src/tools/suggest-google-business-review-reply.ts +5 -4
- package/src/tools/test-webhook.ts +7 -1
- package/src/tools/trigger-platform-helper.ts +5 -1
- package/src/tools/update-oauth-client.ts +9 -2
- package/src/tools/update-post-release-id.ts +5 -2
- package/src/tools/update-post-status.ts +7 -1
- package/src/tools/update-post.ts +10 -5
- package/src/tools/update-webhook.ts +7 -2
- package/src/tools/upload-media-from-url.ts +7 -9
- package/src/tools/upload-media.ts +6 -2
- package/src/tools/whoami.ts +21 -23
package/dist/tools/get-brand.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, formatDateTime, mdKeyValue, mdTitle } from '../lib/format.js';
|
|
2
3
|
export const getBrandTool = {
|
|
3
4
|
name: 'get_brand',
|
|
4
5
|
description: 'Get one brand/client by ID. Returns its workspace, source, linked legacy brand group if present, and the number of social accounts assigned to it.',
|
|
@@ -8,21 +9,18 @@ export const getBrandTool = {
|
|
|
8
9
|
async execute(client, input) {
|
|
9
10
|
const result = await client.getBrand(input.brand_id);
|
|
10
11
|
const brand = result.brand;
|
|
11
|
-
|
|
12
|
-
`Brand: ${brand.name}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (brand.updated_at)
|
|
25
|
-
lines.push(`• Updated: ${brand.updated_at}`);
|
|
26
|
-
return lines.join('\n');
|
|
12
|
+
return [
|
|
13
|
+
mdTitle(`Brand: ${brand.name}`),
|
|
14
|
+
mdKeyValue([
|
|
15
|
+
['Brand ID', code(brand.id)],
|
|
16
|
+
['Source', brand.source],
|
|
17
|
+
['Workspace', brand.workspace_id ? code(brand.workspace_id) : 'n/a'],
|
|
18
|
+
['Accounts assigned', brand.account_count ?? 0],
|
|
19
|
+
['Workspace client ID', brand.workspace_client_id ? code(brand.workspace_client_id) : undefined],
|
|
20
|
+
['Legacy brand group ID', brand.legacy_brand_group_id ? code(brand.legacy_brand_group_id) : undefined],
|
|
21
|
+
['Created', formatDateTime(brand.created_at)],
|
|
22
|
+
['Updated', formatDateTime(brand.updated_at)],
|
|
23
|
+
]),
|
|
24
|
+
].join('\n\n');
|
|
27
25
|
},
|
|
28
26
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { CONNECT_INPUTS, PLANNED_PLATFORM_IDS } from '../generated/platform-manifest.js';
|
|
3
|
+
import { code, mdJson, mdKeyValue, mdSection, mdTable, mdTitle, statusLabel } from '../lib/format.js';
|
|
3
4
|
export const getConnectLinkTool = {
|
|
4
5
|
name: 'get_connect_link',
|
|
5
6
|
description: 'List dashboard handoff links/readiness for connecting social accounts, or get one platform connection URL. Use connection_url in a logged-in browser. Direct OAuth URLs are intentionally not exposed because provider callbacks rely on browser state.',
|
|
@@ -23,8 +24,18 @@ export const getConnectLinkTool = {
|
|
|
23
24
|
return formatConnectOption(result.connect, true);
|
|
24
25
|
}
|
|
25
26
|
const options = result.connect_options || [];
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
return [
|
|
28
|
+
mdTitle(`Posterly connection options (${options.length})`),
|
|
29
|
+
mdTable(['Provider', 'Target', 'Status', 'Method', 'Connected', 'Readiness'], options.map((option) => [
|
|
30
|
+
option.label,
|
|
31
|
+
code(option.platform),
|
|
32
|
+
statusLabel(option.status),
|
|
33
|
+
option.method,
|
|
34
|
+
option.connected_count ?? option.connected_accounts?.length ?? 0,
|
|
35
|
+
option.missing_env?.length ? 'missing config' : 'configured',
|
|
36
|
+
])),
|
|
37
|
+
'**Tip:** Call this tool with a specific `platform` to get the direct dashboard handoff URL.',
|
|
38
|
+
].join('\n\n');
|
|
28
39
|
},
|
|
29
40
|
};
|
|
30
41
|
function formatConnectOption(option, includeRaw) {
|
|
@@ -36,11 +47,19 @@ function formatConnectOption(option, includeRaw) {
|
|
|
36
47
|
? `; credential fields: ${option.credential_fields.map((field) => field.key).join(', ')}`
|
|
37
48
|
: '';
|
|
38
49
|
const scopes = option.scopes?.length ? `; scopes: ${option.scopes.join(', ')}` : '';
|
|
39
|
-
const raw = includeRaw ?
|
|
50
|
+
const raw = includeRaw ? mdJson('Raw connect option', option) : '';
|
|
40
51
|
return [
|
|
41
|
-
`${option.label} (
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
mdTitle(`${option.label} connection`, code(option.platform)),
|
|
53
|
+
mdKeyValue([
|
|
54
|
+
['Status', statusLabel(option.status)],
|
|
55
|
+
['Method', option.method],
|
|
56
|
+
['Readiness', missing],
|
|
57
|
+
['Connected accounts', accounts],
|
|
58
|
+
['Connection URL', option.connection_url || 'not available'],
|
|
59
|
+
['Credential fields', fields.replace(/^; credential fields: /, '') || undefined],
|
|
60
|
+
['Scopes', scopes.replace(/^; scopes: /, '') || undefined],
|
|
61
|
+
]),
|
|
62
|
+
option.notes?.length ? mdSection('Notes', option.notes.map((note) => `- ${note}`).join('\n')) : '',
|
|
44
63
|
raw,
|
|
45
|
-
].join('\n');
|
|
64
|
+
].filter(Boolean).join('\n\n');
|
|
46
65
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, mdSuccess } from '../lib/format.js';
|
|
2
3
|
export const getGoogleBusinessReviewLinkTool = {
|
|
3
4
|
name: 'get_google_business_review_link',
|
|
4
5
|
description: 'Get the direct public Google review link for a connected Google Business Profile location. Requires account_id or location_id.',
|
|
@@ -9,11 +10,10 @@ export const getGoogleBusinessReviewLinkTool = {
|
|
|
9
10
|
}),
|
|
10
11
|
async execute(client, input) {
|
|
11
12
|
const result = await client.getGoogleBusinessReviewLink(input);
|
|
12
|
-
return [
|
|
13
|
-
|
|
14
|
-
result.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
].join('\n');
|
|
13
|
+
return mdSuccess(`Review link for ${result.businessName}`, [
|
|
14
|
+
['Review link', result.reviewLink],
|
|
15
|
+
['Place ID', code(result.placeId)],
|
|
16
|
+
['Account ID', code(result.account_id)],
|
|
17
|
+
]);
|
|
18
18
|
},
|
|
19
19
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { ALL_PLATFORM_INPUTS, SUPPORTED_PLATFORM_IDS } from '../generated/platform-manifest.js';
|
|
3
|
+
import { code, mdJson, mdKeyValue, mdSection, mdTable, mdTitle, statusLabel } from '../lib/format.js';
|
|
3
4
|
export const getPlatformSchemaTool = {
|
|
4
5
|
name: 'get_platform_schema',
|
|
5
6
|
description: 'Get the platform schema for a specific connected account or platform. Call this before creating posts with platform_settings so you know allowed post types, media limits, helper tools, and field names.',
|
|
@@ -18,27 +19,33 @@ export const getPlatformSchemaTool = {
|
|
|
18
19
|
const result = await client.getPlatformSchema(input);
|
|
19
20
|
const schema = result.schema || result.platform || result.platforms?.[0];
|
|
20
21
|
if (!schema)
|
|
21
|
-
return
|
|
22
|
+
return mdJson('Platform schema response', result);
|
|
22
23
|
const fields = schema.settings_schema?.fields || [];
|
|
23
24
|
const helpers = schema.helper_tools || [];
|
|
24
|
-
const accountLine = result.account
|
|
25
|
-
? `Account: ${result.account.username || result.account.id} (${result.account.platform})\n`
|
|
26
|
-
: '';
|
|
27
25
|
return [
|
|
28
|
-
`${schema.label} (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
mdTitle(`${schema.label} schema`, code(schema.id)),
|
|
27
|
+
mdKeyValue([
|
|
28
|
+
['Account', result.account ? `${result.account.username || result.account.id} (${result.account.platform})` : undefined],
|
|
29
|
+
['Status', statusLabel(schema.status)],
|
|
30
|
+
['Post types', schema.post_types.join(', ') || 'n/a'],
|
|
31
|
+
['Content limit', schema.content_limit ?? 'n/a'],
|
|
32
|
+
['Analytics', schema.analytics_supported],
|
|
33
|
+
]),
|
|
35
34
|
fields.length
|
|
36
|
-
?
|
|
37
|
-
|
|
35
|
+
? mdSection('Settings', mdTable(['Field', 'Required', 'Description'], fields.map((field) => [
|
|
36
|
+
code(field.key),
|
|
37
|
+
Boolean(field.required),
|
|
38
|
+
field.description || field.label || 'n/a',
|
|
39
|
+
])))
|
|
40
|
+
: mdSection('Settings', 'None'),
|
|
38
41
|
helpers.length
|
|
39
|
-
?
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
? mdSection('Helpers', mdTable(['Helper', 'Description'], helpers.map((helper) => [
|
|
43
|
+
code(helper.name),
|
|
44
|
+
helper.description || 'n/a',
|
|
45
|
+
])))
|
|
46
|
+
: mdSection('Helpers', 'None'),
|
|
47
|
+
mdJson('Media rules', schema.media),
|
|
48
|
+
mdJson('Example settings', schema.settings_schema?.example || {}),
|
|
42
49
|
].filter(Boolean).join('\n');
|
|
43
50
|
},
|
|
44
51
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, compactText, formatDateTime, formatNumber, mdEmpty, mdSection, mdTable, mdTitle } from '../lib/format.js';
|
|
2
3
|
export const getPostAnalyticsTool = {
|
|
3
4
|
name: 'get_post_analytics',
|
|
4
5
|
description: 'Get per-post engagement metrics (likes, comments, reach, impressions, saves, shares, plays, clicks, watch time) for a connected social account. Supports Instagram, Facebook, LinkedIn, Google Business Profile, Pinterest, and YouTube. Returns the most recent posts first.',
|
|
@@ -26,56 +27,58 @@ export const getPostAnalyticsTool = {
|
|
|
26
27
|
const result = await client.getPostAnalytics(input);
|
|
27
28
|
const { account, range, posts, total } = result;
|
|
28
29
|
if (posts.length === 0) {
|
|
29
|
-
return `No analytics found for @${account.username} (${account.platform}) between ${range.from} and ${range.to}
|
|
30
|
+
return mdEmpty('post analytics', `No post analytics were found for @${account.username} (${account.platform}) between ${range.from} and ${range.to}.`);
|
|
30
31
|
}
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (p.saved)
|
|
53
|
-
metrics.push(`${p.saved} saved`);
|
|
54
|
-
if (p.shares)
|
|
55
|
-
metrics.push(`${p.shares} shares`);
|
|
56
|
-
if (p.plays)
|
|
57
|
-
metrics.push(`${p.plays.toLocaleString()} plays`);
|
|
58
|
-
if (account.platform === 'youtube' && p.total_watch_time_ms) {
|
|
59
|
-
metrics.push(`${Math.round(p.total_watch_time_ms / 60000).toLocaleString()} watch minutes`);
|
|
60
|
-
}
|
|
61
|
-
if (account.platform === 'youtube' && p.avg_watch_time_ms) {
|
|
62
|
-
metrics.push(`${Math.round(p.avg_watch_time_ms / 1000)}s avg view`);
|
|
63
|
-
}
|
|
64
|
-
if (p.url_link_clicks) {
|
|
65
|
-
metrics.push(account.platform === 'pinterest'
|
|
66
|
-
? `${p.url_link_clicks.toLocaleString()} outbound clicks`
|
|
67
|
-
: `${p.url_link_clicks.toLocaleString()} link clicks`);
|
|
68
|
-
}
|
|
69
|
-
if (p.user_profile_clicks) {
|
|
70
|
-
metrics.push(account.platform === 'pinterest'
|
|
71
|
-
? `${p.user_profile_clicks.toLocaleString()} pin clicks`
|
|
72
|
-
: `${p.user_profile_clicks.toLocaleString()} profile clicks`);
|
|
73
|
-
}
|
|
74
|
-
lines.push(`• [${postedAt}] ${caption}`);
|
|
75
|
-
lines.push(` ${metrics.join(' • ')}`);
|
|
76
|
-
if (p.permalink)
|
|
77
|
-
lines.push(` ${p.permalink}`);
|
|
78
|
-
}
|
|
79
|
-
return lines.join('\n');
|
|
32
|
+
const totals = posts.reduce((acc, post) => ({
|
|
33
|
+
likes: acc.likes + post.likes,
|
|
34
|
+
comments: acc.comments + post.comments,
|
|
35
|
+
reach: acc.reach + post.reach,
|
|
36
|
+
impressions: acc.impressions + post.impressions,
|
|
37
|
+
shares: acc.shares + post.shares,
|
|
38
|
+
saves: acc.saves + post.saved,
|
|
39
|
+
}), { likes: 0, comments: 0, reach: 0, impressions: 0, shares: 0, saves: 0 });
|
|
40
|
+
return [
|
|
41
|
+
mdTitle(`📈 Post analytics for @${account.username}`, `${account.platform} · ${range.from} → ${range.to} · showing ${posts.length} of ${total}`),
|
|
42
|
+
mdSection('Returned totals', mdTable(['Likes', 'Comments', 'Reach', 'Impressions', 'Shares', 'Saves'], [[totals.likes, totals.comments, totals.reach, totals.impressions, totals.shares, totals.saves]])),
|
|
43
|
+
mdSection('Posts', mdTable(['Posted', 'Post', 'Caption', 'Reach', 'Likes', 'Comments', 'Extras'], posts.map((post) => [
|
|
44
|
+
formatDateTime(post.posted_at),
|
|
45
|
+
post.id ? code(post.id) : code(post.platform_media_id),
|
|
46
|
+
compactText(post.caption_snippet, 70) || '(no caption)',
|
|
47
|
+
formatNumber(post.reach),
|
|
48
|
+
formatNumber(post.likes),
|
|
49
|
+
formatNumber(post.comments),
|
|
50
|
+
buildExtras(account.platform, post),
|
|
51
|
+
]))),
|
|
52
|
+
].join('\n\n');
|
|
80
53
|
},
|
|
81
54
|
};
|
|
55
|
+
function buildExtras(platform, post) {
|
|
56
|
+
const metrics = [];
|
|
57
|
+
if (post.impressions)
|
|
58
|
+
metrics.push(`${formatNumber(post.impressions)} impressions`);
|
|
59
|
+
if (post.saved)
|
|
60
|
+
metrics.push(`${formatNumber(post.saved)} saved`);
|
|
61
|
+
if (post.shares)
|
|
62
|
+
metrics.push(`${formatNumber(post.shares)} shares`);
|
|
63
|
+
if (post.plays)
|
|
64
|
+
metrics.push(`${formatNumber(post.plays)} plays`);
|
|
65
|
+
if (platform === 'youtube' && post.total_watch_time_ms) {
|
|
66
|
+
metrics.push(`${formatNumber(Math.round(post.total_watch_time_ms / 60000))} watch minutes`);
|
|
67
|
+
}
|
|
68
|
+
if (platform === 'youtube' && post.avg_watch_time_ms) {
|
|
69
|
+
metrics.push(`${formatNumber(Math.round(post.avg_watch_time_ms / 1000))}s avg view`);
|
|
70
|
+
}
|
|
71
|
+
if (post.url_link_clicks) {
|
|
72
|
+
metrics.push(platform === 'pinterest'
|
|
73
|
+
? `${formatNumber(post.url_link_clicks)} outbound clicks`
|
|
74
|
+
: `${formatNumber(post.url_link_clicks)} link clicks`);
|
|
75
|
+
}
|
|
76
|
+
if (post.user_profile_clicks) {
|
|
77
|
+
metrics.push(platform === 'pinterest'
|
|
78
|
+
? `${formatNumber(post.user_profile_clicks)} pin clicks`
|
|
79
|
+
: `${formatNumber(post.user_profile_clicks)} profile clicks`);
|
|
80
|
+
}
|
|
81
|
+
if (post.permalink)
|
|
82
|
+
metrics.push(post.permalink);
|
|
83
|
+
return metrics.join(' · ') || 'n/a';
|
|
84
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, mdJson, mdTitle } from '../lib/format.js';
|
|
2
3
|
export const getPostMissingTool = {
|
|
3
4
|
name: 'get_post_missing',
|
|
4
5
|
description: 'Inspect whether a post is missing required content, media, account, platform settings, or metadata before it can publish. Use this to repair failed or imported posts.',
|
|
@@ -7,6 +8,9 @@ export const getPostMissingTool = {
|
|
|
7
8
|
}),
|
|
8
9
|
async execute(client, input) {
|
|
9
10
|
const result = await client.getPostMissing(input.post_id);
|
|
10
|
-
return
|
|
11
|
+
return [
|
|
12
|
+
mdTitle(`Post readiness for ${code(input.post_id)}`),
|
|
13
|
+
mdJson('Missing fields response', result),
|
|
14
|
+
].join('\n\n');
|
|
11
15
|
},
|
|
12
16
|
};
|
package/dist/tools/get-post.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, formatDateTime, mdKeyValue, mdQuote, mdSection, mdTitle, statusLabel } from '../lib/format.js';
|
|
2
3
|
export const getPostTool = {
|
|
3
4
|
name: 'get_post',
|
|
4
5
|
description: 'Get details of a specific post by ID. Returns caption, status, scheduled time, media, and platform info.',
|
|
@@ -8,19 +9,20 @@ export const getPostTool = {
|
|
|
8
9
|
async execute(client, input) {
|
|
9
10
|
const result = await client.getPost(input.post_id);
|
|
10
11
|
const p = result.post;
|
|
11
|
-
|
|
12
|
-
`Post
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
return [
|
|
13
|
+
mdTitle(`Post ${code(p.id)}`),
|
|
14
|
+
mdKeyValue([
|
|
15
|
+
['Status', statusLabel(p.status)],
|
|
16
|
+
['Type', p.post_type || 'unknown'],
|
|
17
|
+
['Scheduled', formatDateTime(p.scheduled_at)],
|
|
18
|
+
['Published URL', p.platform_post_url],
|
|
19
|
+
['Media', p.media_url],
|
|
20
|
+
['Media count', p.media_urls?.length],
|
|
21
|
+
]),
|
|
22
|
+
mdSection('Caption', p.content ? mdQuote(p.content) : '_(empty)_'),
|
|
23
|
+
p.media_urls?.length
|
|
24
|
+
? mdSection('Media URLs', p.media_urls.map((url, index) => `${index + 1}. ${url}`).join('\n'))
|
|
25
|
+
: '',
|
|
26
|
+
].filter(Boolean).join('\n\n');
|
|
25
27
|
},
|
|
26
28
|
};
|
|
@@ -8,11 +8,11 @@ export declare const getVideoJobTool: {
|
|
|
8
8
|
status: z.ZodOptional<z.ZodEnum<["pending", "processing", "completed", "failed", "dismissed"]>>;
|
|
9
9
|
limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
10
10
|
}, "strip", z.ZodTypeAny, {
|
|
11
|
-
status?: "
|
|
11
|
+
status?: "completed" | "pending" | "processing" | "failed" | "dismissed" | undefined;
|
|
12
12
|
limit?: number | undefined;
|
|
13
13
|
job_id?: string | undefined;
|
|
14
14
|
}, {
|
|
15
|
-
status?: "
|
|
15
|
+
status?: "completed" | "pending" | "processing" | "failed" | "dismissed" | undefined;
|
|
16
16
|
limit?: number | undefined;
|
|
17
17
|
job_id?: string | undefined;
|
|
18
18
|
}>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, compactText, formatDateTime, mdJson, mdKeyValue, mdSection, mdTable, mdTitle, statusLabel } from '../lib/format.js';
|
|
2
3
|
export const getVideoJobTool = {
|
|
3
4
|
name: 'get_video_job',
|
|
4
5
|
description: 'Get one Veo video generation job by ID, or list recent video jobs when job_id is omitted. Use after generate_video to poll for completed video_url.',
|
|
@@ -13,19 +14,35 @@ export const getVideoJobTool = {
|
|
|
13
14
|
return formatJob(result.job, true);
|
|
14
15
|
const jobs = result.jobs || [];
|
|
15
16
|
return [
|
|
16
|
-
`Video jobs (${jobs.length})
|
|
17
|
-
jobs.map((job) =>
|
|
17
|
+
mdTitle(`Video jobs (${jobs.length})`),
|
|
18
|
+
mdTable(['Job ID', 'Status', 'Prompt', 'Model', 'Created', 'Cost'], jobs.map((job) => [
|
|
19
|
+
code(job.id),
|
|
20
|
+
statusLabel(job.status),
|
|
21
|
+
compactText(job.prompt, 70),
|
|
22
|
+
job.model,
|
|
23
|
+
formatDateTime(job.created_at),
|
|
24
|
+
job.credit_cost,
|
|
25
|
+
])),
|
|
18
26
|
].filter(Boolean).join('\n');
|
|
19
27
|
},
|
|
20
28
|
};
|
|
21
29
|
function formatJob(job, includeRaw) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
return [
|
|
31
|
+
mdTitle(`Video job ${code(job.id)}`),
|
|
32
|
+
mdKeyValue([
|
|
33
|
+
['Status', statusLabel(job.status)],
|
|
34
|
+
['Model', job.model],
|
|
35
|
+
['Duration', `${job.duration_seconds}s`],
|
|
36
|
+
['Aspect ratio', job.aspect_ratio],
|
|
37
|
+
['Resolution', job.resolution || 'n/a'],
|
|
38
|
+
['Credit cost', job.credit_cost],
|
|
39
|
+
['Created', formatDateTime(job.created_at)],
|
|
40
|
+
['Started', formatDateTime(job.started_at)],
|
|
41
|
+
['Completed', formatDateTime(job.completed_at)],
|
|
42
|
+
['Video URL', job.video_url || undefined],
|
|
43
|
+
['Error', job.error_message || undefined],
|
|
44
|
+
]),
|
|
45
|
+
mdSection('Prompt', job.prompt),
|
|
46
|
+
includeRaw ? mdJson('Raw job', job) : '',
|
|
47
|
+
].filter(Boolean).join('\n\n');
|
|
31
48
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, mdJson, mdKeyValue, mdSection, mdTable, mdTitle } from '../lib/format.js';
|
|
2
3
|
export const getVideoOptionsTool = {
|
|
3
4
|
name: 'get_video_options',
|
|
4
5
|
description: 'List read-only Veo video generation options, input modes, durations, resolutions, aspect ratios, and credit-cost estimates. This does not generate video or spend credits.',
|
|
@@ -7,20 +8,25 @@ export const getVideoOptionsTool = {
|
|
|
7
8
|
const options = await client.getVideoOptions();
|
|
8
9
|
const models = Array.isArray(options.models) ? options.models : [];
|
|
9
10
|
const inputModes = Array.isArray(options.input_modes) ? options.input_modes : [];
|
|
10
|
-
|
|
11
|
-
'Video generation options',
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
return [
|
|
12
|
+
mdTitle('Video generation options'),
|
|
13
|
+
mdKeyValue([
|
|
14
|
+
['Provider', options.provider || 'unknown'],
|
|
15
|
+
['Public generation status', options.public_generation_status || 'unknown'],
|
|
16
|
+
['Durations', `${(options.durations_seconds || []).join(', ')} seconds`],
|
|
17
|
+
['Resolutions', (options.resolutions || []).join(', ')],
|
|
18
|
+
['Aspect ratios', (options.aspect_ratios || []).join(', ')],
|
|
19
|
+
]),
|
|
20
|
+
mdSection('Models', mdTable(['Model', 'Label', 'Credit cost'], models.map((model) => [
|
|
21
|
+
code(model.id),
|
|
22
|
+
model.label || model.name || 'n/a',
|
|
23
|
+
model.credit_cost ?? model.cost ?? 'n/a',
|
|
24
|
+
]))),
|
|
25
|
+
mdSection('Input modes', mdTable(['Mode', 'Label'], inputModes.map((mode) => [
|
|
26
|
+
code(mode.id),
|
|
27
|
+
mode.label || mode.name || 'n/a',
|
|
28
|
+
]))),
|
|
29
|
+
mdJson('Raw options', options),
|
|
30
|
+
].filter(Boolean).join('\n\n');
|
|
25
31
|
},
|
|
26
32
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, mdKeyValue, mdTitle, statusLabel } from '../lib/format.js';
|
|
2
3
|
export const getXPostingQuotaTool = {
|
|
3
4
|
name: 'get_x_posting_quota',
|
|
4
5
|
description: 'Get managed X posting quota for a workspace, including total monthly allowance, used posts, remaining posts, and URL-blocking status.',
|
|
@@ -8,14 +9,16 @@ export const getXPostingQuotaTool = {
|
|
|
8
9
|
async execute(client, input) {
|
|
9
10
|
const quota = await client.getXPostingQuota(input);
|
|
10
11
|
return [
|
|
11
|
-
'X posting quota',
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
mdTitle('X posting quota'),
|
|
13
|
+
mdKeyValue([
|
|
14
|
+
['Workspace', quota.workspace_id ? code(quota.workspace_id) : 'default'],
|
|
15
|
+
['Tier', quota.tier || 'unknown'],
|
|
16
|
+
['Total posts', quota.total_posts],
|
|
17
|
+
['Used posts', quota.used_posts],
|
|
18
|
+
['Remaining posts', quota.remaining_posts],
|
|
19
|
+
['URL posting', statusLabel(quota.urls_blocked ? 'blocked' : 'available')],
|
|
20
|
+
['Period', `${quota.current_period_start} → ${quota.current_period_end}`],
|
|
21
|
+
]),
|
|
22
|
+
].join('\n\n');
|
|
20
23
|
},
|
|
21
24
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, mdEmpty, mdTable, mdTitle } from '../lib/format.js';
|
|
2
3
|
export const listAccountsTool = {
|
|
3
4
|
name: 'list_accounts',
|
|
4
5
|
description: 'List connected social media accounts. Returns platform, username, account ID, and workspace ID for each. Pass workspace_id to filter to a specific workspace; otherwise returns all accounts the caller owns across every workspace.',
|
|
@@ -12,10 +13,17 @@ export const listAccountsTool = {
|
|
|
12
13
|
const accounts = await client.listAccounts({ workspace_id: input.workspace_id });
|
|
13
14
|
if (accounts.length === 0) {
|
|
14
15
|
return input.workspace_id
|
|
15
|
-
? '
|
|
16
|
-
: '
|
|
16
|
+
? mdEmpty('connected accounts', 'This workspace does not have any social accounts connected yet.')
|
|
17
|
+
: mdEmpty('connected accounts', 'Posterly does not see any connected social accounts yet.', 'Connect accounts in the Posterly dashboard, then run this again.');
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
return [
|
|
20
|
+
mdTitle(`Connected accounts (${accounts.length})`),
|
|
21
|
+
mdTable(['Platform', 'Account', 'Account ID', 'Workspace'], accounts.map((account) => [
|
|
22
|
+
account.platform,
|
|
23
|
+
`@${account.username}`,
|
|
24
|
+
code(account.id),
|
|
25
|
+
account.workspace_id ? code(account.workspace_id) : 'default',
|
|
26
|
+
])),
|
|
27
|
+
].join('\n\n');
|
|
20
28
|
},
|
|
21
29
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { compactText, formatDateTime, mdEmpty, mdTable, mdTitle } from '../lib/format.js';
|
|
2
3
|
export const listActivityTool = {
|
|
3
4
|
name: 'list_activity',
|
|
4
5
|
description: 'List recent activity and publish events for posts, including status changes, publish attempts, failures, and retries. Use this as the agent notifications feed.',
|
|
@@ -12,24 +13,18 @@ export const listActivityTool = {
|
|
|
12
13
|
async execute(client, input) {
|
|
13
14
|
const result = await client.listActivity(input);
|
|
14
15
|
if (result.items.length === 0) {
|
|
15
|
-
return 'No activity
|
|
16
|
+
return mdEmpty('activity', 'No activity matched those filters.');
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
-
`Activity (${result.items.length} of ${result.total})
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
else {
|
|
29
|
-
const label = item.description || item.action || 'activity';
|
|
30
|
-
lines.push(`- [${when}] post #${item.post_id || '?'} ${label}`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return lines.join('\n');
|
|
18
|
+
return [
|
|
19
|
+
mdTitle(`Activity (${result.items.length} of ${result.total})`),
|
|
20
|
+
mdTable(['When', 'Source', 'Post', 'Platform', 'Event', 'Detail'], result.items.map((item) => [
|
|
21
|
+
formatDateTime(item.created_at),
|
|
22
|
+
item.type,
|
|
23
|
+
item.post_id ?? '?',
|
|
24
|
+
item.platform || 'n/a',
|
|
25
|
+
item.type === 'publish' ? item.outcome || 'publish' : item.action || 'activity',
|
|
26
|
+
compactText(item.error_message || item.description, 120) || 'n/a',
|
|
27
|
+
])),
|
|
28
|
+
].join('\n\n');
|
|
34
29
|
},
|
|
35
30
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { code, mdEmpty, mdTable, mdTitle } from '../lib/format.js';
|
|
2
3
|
export const listBrandAccountsTool = {
|
|
3
4
|
name: 'list_brand_accounts',
|
|
4
5
|
description: 'List the connected social accounts assigned to a brand/client. Use this when a user refers to a brand name rather than a raw account handle.',
|
|
@@ -8,9 +9,16 @@ export const listBrandAccountsTool = {
|
|
|
8
9
|
async execute(client, input) {
|
|
9
10
|
const accounts = await client.listBrandAccounts(input.brand_id);
|
|
10
11
|
if (accounts.length === 0) {
|
|
11
|
-
return 'No social accounts are currently assigned to this brand.';
|
|
12
|
+
return mdEmpty('brand accounts', 'No social accounts are currently assigned to this brand.');
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
return [
|
|
15
|
+
mdTitle(`Brand accounts (${accounts.length})`),
|
|
16
|
+
mdTable(['Platform', 'Account', 'Account ID', 'Workspace'], accounts.map((account) => [
|
|
17
|
+
account.platform,
|
|
18
|
+
`@${account.username}`,
|
|
19
|
+
code(account.id),
|
|
20
|
+
account.workspace_id ? code(account.workspace_id) : 'default',
|
|
21
|
+
])),
|
|
22
|
+
].join('\n\n');
|
|
15
23
|
},
|
|
16
24
|
};
|