posterly-mcp-server 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -1
- package/SUBMISSIONS.md +73 -0
- package/dist/index.js +80 -0
- package/dist/lib/api-client.d.ts +73 -0
- package/dist/lib/api-client.js +73 -0
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/tools/get-comment.d.ts +20 -0
- package/dist/tools/get-comment.js +36 -0
- package/dist/tools/get-conversation.d.ts +24 -0
- package/dist/tools/get-conversation.js +31 -0
- package/dist/tools/list-comments.d.ts +40 -0
- package/dist/tools/list-comments.js +43 -0
- package/dist/tools/list-conversations.d.ts +40 -0
- package/dist/tools/list-conversations.js +35 -0
- package/dist/tools/reply-to-comment.d.ts +32 -0
- package/dist/tools/reply-to-comment.js +17 -0
- package/dist/tools/send-message.d.ts +28 -0
- package/dist/tools/send-message.js +16 -0
- package/dist/tools/sync-inbox.d.ts +24 -0
- package/dist/tools/sync-inbox.js +18 -0
- package/dist/tools/update-comment.d.ts +28 -0
- package/dist/tools/update-comment.js +21 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/index.ts +128 -0
- package/src/lib/api-client.ts +144 -0
- package/src/lib/version.ts +1 -1
- package/src/tools/get-comment.ts +47 -0
- package/src/tools/get-conversation.ts +44 -0
- package/src/tools/list-comments.ts +59 -0
- package/src/tools/list-conversations.ts +54 -0
- package/src/tools/reply-to-comment.ts +30 -0
- package/src/tools/send-message.ts +28 -0
- package/src/tools/sync-inbox.ts +28 -0
- package/src/tools/update-comment.ts +31 -0
- package/tool-annotations.json +21 -4
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
import { truncateText } from '../lib/format.js';
|
|
4
|
+
|
|
5
|
+
const INBOX_PLATFORMS = ['instagram', 'facebook', 'threads'] as const;
|
|
6
|
+
|
|
7
|
+
export const listCommentsTool = {
|
|
8
|
+
name: 'list_comments',
|
|
9
|
+
description:
|
|
10
|
+
'List social inbox comments and replies for Instagram, Facebook Pages, and Threads. Filter by account, platform, unread/flagged, or search. Requires a Pro plan or higher plus posts:read.',
|
|
11
|
+
inputSchema: z.object({
|
|
12
|
+
workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
|
|
13
|
+
account_id: z.number().int().positive().optional().describe('Social account ID from list_accounts.'),
|
|
14
|
+
platform: z.enum(INBOX_PLATFORMS).optional().describe('instagram, facebook, or threads.'),
|
|
15
|
+
filter: z.enum(['all', 'unread', 'flagged']).optional().describe('Comment filter. Defaults to all.'),
|
|
16
|
+
search: z.string().optional().describe('Search comment content or author username.'),
|
|
17
|
+
limit: z.number().int().min(1).max(100).optional().describe('Number of comments to return. Default 50, max 100.'),
|
|
18
|
+
offset: z.number().int().min(0).optional().describe('Skip this many comments before returning results.'),
|
|
19
|
+
}),
|
|
20
|
+
|
|
21
|
+
async execute(
|
|
22
|
+
client: PosterlyClient,
|
|
23
|
+
input: {
|
|
24
|
+
workspace_id?: string;
|
|
25
|
+
account_id?: number;
|
|
26
|
+
platform?: 'instagram' | 'facebook' | 'threads';
|
|
27
|
+
filter?: 'all' | 'unread' | 'flagged';
|
|
28
|
+
search?: string;
|
|
29
|
+
limit?: number;
|
|
30
|
+
offset?: number;
|
|
31
|
+
},
|
|
32
|
+
) {
|
|
33
|
+
const result = await client.listComments(input);
|
|
34
|
+
if (result.comments.length === 0) {
|
|
35
|
+
return 'No inbox comments found matching your filters.';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const lines = [`Inbox comments (showing ${result.comments.length} of ${result.total}):`];
|
|
39
|
+
for (const comment of result.comments) {
|
|
40
|
+
const author = typeof comment.author_username === 'string' ? `@${comment.author_username}` : 'unknown';
|
|
41
|
+
const platform = typeof comment.platform === 'string' ? comment.platform : 'unknown';
|
|
42
|
+
const flags: string[] = [];
|
|
43
|
+
if (comment.is_read === false) flags.push('unread');
|
|
44
|
+
if (comment.is_hidden) flags.push('hidden');
|
|
45
|
+
if (comment.is_flagged) flags.push('flagged');
|
|
46
|
+
const flagLabel = flags.length > 0 ? `, ${flags.join(', ')}` : '';
|
|
47
|
+
const text = truncateText(typeof comment.content === 'string' ? comment.content : '', 80);
|
|
48
|
+
const replies = Array.isArray(comment.replies) ? comment.replies.length : 0;
|
|
49
|
+
lines.push(`- ${String(comment.id)} (${platform}, ${author}${flagLabel}${replies ? `, ${replies} replies` : ''}): ${text}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (result.hasMore) {
|
|
53
|
+
const nextOffset = (input.offset ?? 0) + result.comments.length;
|
|
54
|
+
lines.push('', `More comments available. Call again with offset: ${nextOffset}.`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return lines.join('\n');
|
|
58
|
+
},
|
|
59
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
import { truncateText } from '../lib/format.js';
|
|
4
|
+
|
|
5
|
+
const INBOX_PLATFORMS = ['instagram', 'facebook', 'threads'] as const;
|
|
6
|
+
|
|
7
|
+
export const listConversationsTool = {
|
|
8
|
+
name: 'list_conversations',
|
|
9
|
+
description:
|
|
10
|
+
'List social inbox DM conversations for Instagram and Facebook Page inboxes. Filter by account, platform, unread/starred/archived, or search. Requires a Pro plan or higher plus posts:read. Threads has no DMs.',
|
|
11
|
+
inputSchema: z.object({
|
|
12
|
+
workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
|
|
13
|
+
account_id: z.number().int().positive().optional().describe('Social account ID from list_accounts.'),
|
|
14
|
+
platform: z.enum(INBOX_PLATFORMS).optional().describe('instagram or facebook. Threads has no DMs.'),
|
|
15
|
+
filter: z.enum(['all', 'unread', 'starred', 'archived']).optional().describe('Conversation filter. Defaults to all (excludes archived).'),
|
|
16
|
+
search: z.string().optional().describe('Search participant username or last message preview.'),
|
|
17
|
+
limit: z.number().int().min(1).max(100).optional().describe('Number of conversations to return. Default 50, max 100.'),
|
|
18
|
+
offset: z.number().int().min(0).optional().describe('Skip this many conversations before returning results.'),
|
|
19
|
+
}),
|
|
20
|
+
|
|
21
|
+
async execute(
|
|
22
|
+
client: PosterlyClient,
|
|
23
|
+
input: {
|
|
24
|
+
workspace_id?: string;
|
|
25
|
+
account_id?: number;
|
|
26
|
+
platform?: 'instagram' | 'facebook' | 'threads';
|
|
27
|
+
filter?: 'all' | 'unread' | 'starred' | 'archived';
|
|
28
|
+
search?: string;
|
|
29
|
+
limit?: number;
|
|
30
|
+
offset?: number;
|
|
31
|
+
},
|
|
32
|
+
) {
|
|
33
|
+
const result = await client.listConversations(input);
|
|
34
|
+
if (result.conversations.length === 0) {
|
|
35
|
+
return 'No inbox conversations found matching your filters.';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const lines = [`Inbox conversations (showing ${result.conversations.length} of ${result.total}):`];
|
|
39
|
+
for (const conversation of result.conversations) {
|
|
40
|
+
const username = typeof conversation.participant_username === 'string' ? `@${conversation.participant_username}` : 'unknown';
|
|
41
|
+
const platform = typeof conversation.platform === 'string' ? conversation.platform : 'unknown';
|
|
42
|
+
const unread = conversation.is_read === false ? 'unread' : 'read';
|
|
43
|
+
const preview = truncateText(typeof conversation.last_message_preview === 'string' ? conversation.last_message_preview : '', 80);
|
|
44
|
+
lines.push(`- ${String(conversation.id)} (${platform}, ${username}, ${unread}): ${preview}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (result.hasMore) {
|
|
48
|
+
const nextOffset = (input.offset ?? 0) + result.conversations.length;
|
|
49
|
+
lines.push('', `More conversations available. Call again with offset: ${nextOffset}.`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return lines.join('\n');
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
|
|
4
|
+
export const replyToCommentTool = {
|
|
5
|
+
name: 'reply_to_comment',
|
|
6
|
+
description:
|
|
7
|
+
'Reply to an Instagram, Facebook Page, or Threads 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. Requires a Pro plan or higher plus posts:write. Pass confirm=true only after explicit user confirmation.',
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
comment_id: z.string().min(1).describe('Comment ID from list_comments.'),
|
|
10
|
+
content: z.string().min(1).describe('Reply text to post.'),
|
|
11
|
+
type: z.enum(['public', 'private']).optional().describe('public (default) or private. Private is Instagram only.'),
|
|
12
|
+
workspace_id: z.string().optional().describe('Workspace ID from whoami.'),
|
|
13
|
+
confirm: z.literal(true).describe('Must be true after explicit user confirmation.'),
|
|
14
|
+
}),
|
|
15
|
+
|
|
16
|
+
async execute(
|
|
17
|
+
client: PosterlyClient,
|
|
18
|
+
input: {
|
|
19
|
+
comment_id: string;
|
|
20
|
+
content: string;
|
|
21
|
+
type?: 'public' | 'private';
|
|
22
|
+
workspace_id?: string;
|
|
23
|
+
confirm: true;
|
|
24
|
+
},
|
|
25
|
+
) {
|
|
26
|
+
const { confirm: _confirm, ...payload } = input;
|
|
27
|
+
const result = await client.replyToComment(payload);
|
|
28
|
+
return `Comment reply sent (${result.reply_type}, reply_id ${result.reply_id}).`;
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
|
|
4
|
+
export const sendMessageTool = {
|
|
5
|
+
name: 'send_message',
|
|
6
|
+
description:
|
|
7
|
+
'Send a DM reply in an Instagram or Facebook Page inbox conversation. WRITE: posts to the social network. Honor the 24-hour messaging window. Threads has no DMs. Requires a Pro plan or higher plus posts:write. Pass confirm=true only after explicit user confirmation.',
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
conversation_id: z.string().min(1).describe('Conversation ID from list_conversations.'),
|
|
10
|
+
content: z.string().min(1).describe('Message text to send.'),
|
|
11
|
+
workspace_id: z.string().optional().describe('Workspace ID from whoami.'),
|
|
12
|
+
confirm: z.literal(true).describe('Must be true after explicit user confirmation.'),
|
|
13
|
+
}),
|
|
14
|
+
|
|
15
|
+
async execute(
|
|
16
|
+
client: PosterlyClient,
|
|
17
|
+
input: {
|
|
18
|
+
conversation_id: string;
|
|
19
|
+
content: string;
|
|
20
|
+
workspace_id?: string;
|
|
21
|
+
confirm: true;
|
|
22
|
+
},
|
|
23
|
+
) {
|
|
24
|
+
const { confirm: _confirm, ...payload } = input;
|
|
25
|
+
const result = await client.sendMessage(payload);
|
|
26
|
+
return `Message sent (message_id ${result.message_id}).`;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
|
|
4
|
+
export const syncInboxTool = {
|
|
5
|
+
name: 'sync_inbox',
|
|
6
|
+
description:
|
|
7
|
+
'Pull the latest Instagram, Facebook Page, or Threads inbox data for one connected account. Honors a 30-second cooldown. Threads syncs replies only (no DMs). Requires a Pro plan or higher plus posts:write.',
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
account_id: z.number().int().positive().describe('Social account ID from list_accounts.'),
|
|
10
|
+
sync_type: z.enum(['all', 'dm', 'comments']).optional().describe('What to sync. Defaults to all. Threads is comments-only.'),
|
|
11
|
+
workspace_id: z.string().optional().describe('Workspace ID from whoami.'),
|
|
12
|
+
}),
|
|
13
|
+
|
|
14
|
+
async execute(
|
|
15
|
+
client: PosterlyClient,
|
|
16
|
+
input: {
|
|
17
|
+
account_id: number;
|
|
18
|
+
sync_type?: 'all' | 'dm' | 'comments';
|
|
19
|
+
workspace_id?: string;
|
|
20
|
+
},
|
|
21
|
+
) {
|
|
22
|
+
const result = await client.syncInbox(input);
|
|
23
|
+
const parts = ['Inbox sync completed.'];
|
|
24
|
+
const keys = Object.keys(result.results || {});
|
|
25
|
+
if (keys.length > 0) parts.push(`Synced: ${keys.join(', ')}.`);
|
|
26
|
+
return parts.join(' ');
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
|
|
4
|
+
export const updateCommentTool = {
|
|
5
|
+
name: 'update_comment',
|
|
6
|
+
description:
|
|
7
|
+
'Update an inbox comment: hide/unhide it on the platform, or mark it read/unread. Hide is reversible and does not delete the comment. Requires a Pro plan or higher plus posts:write.',
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
comment_id: z.string().min(1).describe('Comment ID from list_comments.'),
|
|
10
|
+
is_hidden: z.boolean().optional().describe('Hide or unhide the comment on Instagram, Facebook, or Threads.'),
|
|
11
|
+
is_read: z.boolean().optional().describe('Mark the comment read or unread in posterly.'),
|
|
12
|
+
workspace_id: z.string().optional().describe('Workspace ID from whoami.'),
|
|
13
|
+
}),
|
|
14
|
+
|
|
15
|
+
async execute(
|
|
16
|
+
client: PosterlyClient,
|
|
17
|
+
input: {
|
|
18
|
+
comment_id: string;
|
|
19
|
+
is_hidden?: boolean;
|
|
20
|
+
is_read?: boolean;
|
|
21
|
+
workspace_id?: string;
|
|
22
|
+
},
|
|
23
|
+
) {
|
|
24
|
+
const result = await client.updateComment(input);
|
|
25
|
+
const comment = result.comment;
|
|
26
|
+
const parts = [`Comment ${String(comment.id)} updated.`];
|
|
27
|
+
if (typeof comment.is_hidden === 'boolean') parts.push(`Hidden: ${comment.is_hidden ? 'yes' : 'no'}.`);
|
|
28
|
+
if (typeof comment.is_read === 'boolean') parts.push(`Read: ${comment.is_read ? 'yes' : 'no'}.`);
|
|
29
|
+
return parts.join(' ');
|
|
30
|
+
},
|
|
31
|
+
};
|
package/tool-annotations.json
CHANGED
|
@@ -77,7 +77,15 @@
|
|
|
77
77
|
"update_webhook",
|
|
78
78
|
"delete_webhook",
|
|
79
79
|
"test_webhook",
|
|
80
|
-
"get_x_posting_quota"
|
|
80
|
+
"get_x_posting_quota",
|
|
81
|
+
"list_conversations",
|
|
82
|
+
"get_conversation",
|
|
83
|
+
"send_message",
|
|
84
|
+
"list_comments",
|
|
85
|
+
"get_comment",
|
|
86
|
+
"reply_to_comment",
|
|
87
|
+
"update_comment",
|
|
88
|
+
"sync_inbox"
|
|
81
89
|
],
|
|
82
90
|
"readOnlyTools": [
|
|
83
91
|
"get_agent_signup_info",
|
|
@@ -117,7 +125,11 @@
|
|
|
117
125
|
"list_activity",
|
|
118
126
|
"get_updates",
|
|
119
127
|
"list_webhooks",
|
|
120
|
-
"get_x_posting_quota"
|
|
128
|
+
"get_x_posting_quota",
|
|
129
|
+
"list_conversations",
|
|
130
|
+
"get_conversation",
|
|
131
|
+
"list_comments",
|
|
132
|
+
"get_comment"
|
|
121
133
|
],
|
|
122
134
|
"destructiveTools": [
|
|
123
135
|
"disconnect_account",
|
|
@@ -158,7 +170,8 @@
|
|
|
158
170
|
"delete_google_business_review_reply",
|
|
159
171
|
"delete_google_business_media",
|
|
160
172
|
"update_webhook",
|
|
161
|
-
"delete_webhook"
|
|
173
|
+
"delete_webhook",
|
|
174
|
+
"update_comment"
|
|
162
175
|
],
|
|
163
176
|
"openWorldTools": [
|
|
164
177
|
"start_signup",
|
|
@@ -201,6 +214,10 @@
|
|
|
201
214
|
"update_webhook",
|
|
202
215
|
"delete_webhook",
|
|
203
216
|
"test_webhook",
|
|
204
|
-
"get_x_posting_quota"
|
|
217
|
+
"get_x_posting_quota",
|
|
218
|
+
"send_message",
|
|
219
|
+
"reply_to_comment",
|
|
220
|
+
"update_comment",
|
|
221
|
+
"sync_inbox"
|
|
205
222
|
]
|
|
206
223
|
}
|