posterly-mcp-server 0.43.6 → 0.43.7
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 +1 -1
- package/SUBMISSIONS.md +9 -0
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/tools/delete-comment.js +1 -1
- package/dist/tools/get-comment.js +1 -1
- package/dist/tools/get-conversation.js +1 -1
- package/dist/tools/list-comments.js +25 -2
- package/dist/tools/list-conversations.js +25 -2
- package/dist/tools/list-posts.js +1 -1
- package/dist/tools/reply-to-comment.js +1 -1
- package/dist/tools/send-message.js +1 -1
- package/dist/tools/sync-inbox.js +1 -1
- package/dist/tools/update-comment.js +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/lib/version.ts +1 -1
- package/src/tools/delete-comment.ts +1 -1
- package/src/tools/get-comment.ts +1 -1
- package/src/tools/get-conversation.ts +1 -1
- package/src/tools/list-comments.ts +25 -2
- package/src/tools/list-conversations.ts +25 -2
- package/src/tools/list-posts.ts +1 -1
- package/src/tools/reply-to-comment.ts +1 -1
- package/src/tools/send-message.ts +1 -1
- package/src/tools/sync-inbox.ts +1 -1
- package/src/tools/update-comment.ts +1 -1
package/README.md
CHANGED
|
@@ -122,7 +122,7 @@ Add the same server definition to your Cursor MCP settings:
|
|
|
122
122
|
|
|
123
123
|
## Available tools
|
|
124
124
|
|
|
125
|
-
`posterly-mcp-server@0.43.
|
|
125
|
+
`posterly-mcp-server@0.43.7` exposes 90 tools.
|
|
126
126
|
|
|
127
127
|
Public setup tools work before `POSTERLY_API_KEY` exists:
|
|
128
128
|
|
package/SUBMISSIONS.md
CHANGED
|
@@ -54,6 +54,15 @@ Use:
|
|
|
54
54
|
refresh tokens (1-hour access, 30-day refresh for DCR clients)
|
|
55
55
|
- Platforms: all 18 production platforms
|
|
56
56
|
|
|
57
|
+
Connectors Connect (the in-product connect form) uses a Client ID Metadata
|
|
58
|
+
Document, not DCR, for this flow:
|
|
59
|
+
|
|
60
|
+
- `client_id`: `https://claude.ai/oauth/mcp-oauth-client-metadata`
|
|
61
|
+
- `redirect_uri`: `https://claude.ai/api/mcp/auth_callback`
|
|
62
|
+
|
|
63
|
+
DCR at `POST /api/oauth/register` remains available for other hosts. It is
|
|
64
|
+
not what Claude Connectors uses for Connect.
|
|
65
|
+
|
|
57
66
|
Do not claim Autopilot ships. Do not claim the connector is already listed.
|
|
58
67
|
|
|
59
68
|
Optional after deploy: bump and publish `posterly-mcp-server` if you want the
|
package/dist/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const POSTERLY_MCP_VERSION = "0.43.
|
|
1
|
+
export declare const POSTERLY_MCP_VERSION = "0.43.7";
|
package/dist/lib/version.js
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
// tool set (minus the intentional pre-auth signup tools that only this stdio
|
|
6
6
|
// package exposes). `npm run check:mcp-parity` enforces both the version match
|
|
7
7
|
// and the tool-list match, and runs in the pre-commit hook.
|
|
8
|
-
export const POSTERLY_MCP_VERSION = '0.43.
|
|
8
|
+
export const POSTERLY_MCP_VERSION = '0.43.7';
|
|
@@ -4,7 +4,7 @@ export const deleteCommentTool = {
|
|
|
4
4
|
description: 'Delete an Instagram or Facebook Page inbox comment. DESTRUCTIVE and IRREVERSIBLE. Threads replies cannot be deleted; hide them with update_comment instead. Requires a Pro plan or higher, posts:write, and admin workspace access. Fetch with get_comment first and 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 or get_comment.'),
|
|
7
|
-
workspace_id: z.string().optional().describe('
|
|
7
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
8
8
|
confirm: z.literal(true).describe('Must be true after explicit user confirmation of the comment deletion.'),
|
|
9
9
|
}),
|
|
10
10
|
async execute(client, input) {
|
|
@@ -5,7 +5,7 @@ export const getCommentTool = {
|
|
|
5
5
|
description: 'Get one social inbox comment and its replies. Instagram, Facebook Page, Threads, and LinkedIn Page comments. Requires a Pro plan or higher plus posts:read.',
|
|
6
6
|
inputSchema: z.object({
|
|
7
7
|
comment_id: z.string().min(1).describe('Comment ID from list_comments.'),
|
|
8
|
-
workspace_id: z.string().optional().describe('
|
|
8
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
9
9
|
}),
|
|
10
10
|
async execute(client, input) {
|
|
11
11
|
const result = await client.getComment(input);
|
|
@@ -5,7 +5,7 @@ export const getConversationTool = {
|
|
|
5
5
|
description: 'Get one social inbox DM conversation and its messages. Instagram and Facebook Page DMs only. Requires a Pro plan or higher plus posts:read.',
|
|
6
6
|
inputSchema: z.object({
|
|
7
7
|
conversation_id: z.string().min(1).describe('Conversation ID from list_conversations.'),
|
|
8
|
-
workspace_id: z.string().optional().describe('
|
|
8
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
9
9
|
limit: z.number().int().min(1).max(100).optional().describe('Maximum messages to return. Default 50, max 100.'),
|
|
10
10
|
}),
|
|
11
11
|
async execute(client, input) {
|
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { truncateText } from '../lib/format.js';
|
|
3
3
|
const INBOX_PLATFORMS = ['instagram', 'facebook', 'threads', 'linkedin'];
|
|
4
|
+
/**
|
|
5
|
+
* The shared `InboxConversationsResponse`/`InboxCommentsResponse` types
|
|
6
|
+
* don't declare `workspaces_skipped` (a plain pass-through field from the
|
|
7
|
+
* API), so it's read here through a loose cast rather than widening those
|
|
8
|
+
* shared types for one caller.
|
|
9
|
+
*/
|
|
10
|
+
function describeSkippedWorkspaces(result) {
|
|
11
|
+
const skipped = result?.workspaces_skipped;
|
|
12
|
+
if (!Array.isArray(skipped) || skipped.length === 0)
|
|
13
|
+
return null;
|
|
14
|
+
const lines = skipped.map((skip) => {
|
|
15
|
+
const name = skip?.name || skip?.workspace_id || 'a workspace';
|
|
16
|
+
const reason = skip?.reason === 'plan' ? "owner's plan does not include inbox" : 'your role there is too low';
|
|
17
|
+
return `- ${name}: ${reason}`;
|
|
18
|
+
});
|
|
19
|
+
return [`Skipped ${skipped.length} workspace${skipped.length === 1 ? '' : 's'} you belong to:`, ...lines].join('\n');
|
|
20
|
+
}
|
|
4
21
|
export const listCommentsTool = {
|
|
5
22
|
name: 'list_comments',
|
|
6
23
|
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
24
|
inputSchema: z.object({
|
|
8
|
-
workspace_id: z.string().optional().describe('
|
|
25
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
9
26
|
account_id: z.number().int().positive().optional().describe('Social account ID from list_accounts.'),
|
|
10
27
|
platform: z.enum(INBOX_PLATFORMS).optional().describe('instagram, facebook, threads, or linkedin.'),
|
|
11
28
|
filter: z.enum(['all', 'unread', 'flagged']).optional().describe('Comment filter. Defaults to all.'),
|
|
@@ -15,8 +32,12 @@ export const listCommentsTool = {
|
|
|
15
32
|
}),
|
|
16
33
|
async execute(client, input) {
|
|
17
34
|
const result = await client.listComments(input);
|
|
35
|
+
const skippedNote = describeSkippedWorkspaces(result);
|
|
18
36
|
if (result.comments.length === 0) {
|
|
19
|
-
return
|
|
37
|
+
return [
|
|
38
|
+
'No inbox comments found matching your filters.',
|
|
39
|
+
skippedNote,
|
|
40
|
+
].filter(Boolean).join('\n\n');
|
|
20
41
|
}
|
|
21
42
|
const lines = [`Inbox comments (showing ${result.comments.length} of ${result.total}):`];
|
|
22
43
|
for (const comment of result.comments) {
|
|
@@ -38,6 +59,8 @@ export const listCommentsTool = {
|
|
|
38
59
|
const nextOffset = (input.offset ?? 0) + result.comments.length;
|
|
39
60
|
lines.push('', `More comments available. Call again with offset: ${nextOffset}.`);
|
|
40
61
|
}
|
|
62
|
+
if (skippedNote)
|
|
63
|
+
lines.push('', skippedNote);
|
|
41
64
|
return lines.join('\n');
|
|
42
65
|
},
|
|
43
66
|
};
|
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { truncateText } from '../lib/format.js';
|
|
3
3
|
const INBOX_PLATFORMS = ['instagram', 'facebook', 'threads'];
|
|
4
|
+
/**
|
|
5
|
+
* The shared `InboxConversationsResponse`/`InboxCommentsResponse` types
|
|
6
|
+
* don't declare `workspaces_skipped` (a plain pass-through field from the
|
|
7
|
+
* API), so it's read here through a loose cast rather than widening those
|
|
8
|
+
* shared types for one caller.
|
|
9
|
+
*/
|
|
10
|
+
function describeSkippedWorkspaces(result) {
|
|
11
|
+
const skipped = result?.workspaces_skipped;
|
|
12
|
+
if (!Array.isArray(skipped) || skipped.length === 0)
|
|
13
|
+
return null;
|
|
14
|
+
const lines = skipped.map((skip) => {
|
|
15
|
+
const name = skip?.name || skip?.workspace_id || 'a workspace';
|
|
16
|
+
const reason = skip?.reason === 'plan' ? "owner's plan does not include inbox" : 'your role there is too low';
|
|
17
|
+
return `- ${name}: ${reason}`;
|
|
18
|
+
});
|
|
19
|
+
return [`Skipped ${skipped.length} workspace${skipped.length === 1 ? '' : 's'} you belong to:`, ...lines].join('\n');
|
|
20
|
+
}
|
|
4
21
|
export const listConversationsTool = {
|
|
5
22
|
name: 'list_conversations',
|
|
6
23
|
description: '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.',
|
|
7
24
|
inputSchema: z.object({
|
|
8
|
-
workspace_id: z.string().optional().describe('
|
|
25
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
9
26
|
account_id: z.number().int().positive().optional().describe('Social account ID from list_accounts.'),
|
|
10
27
|
platform: z.enum(INBOX_PLATFORMS).optional().describe('instagram or facebook. Threads has no DMs.'),
|
|
11
28
|
filter: z.enum(['all', 'unread', 'starred', 'archived']).optional().describe('Conversation filter. Defaults to all (excludes archived).'),
|
|
@@ -15,8 +32,12 @@ export const listConversationsTool = {
|
|
|
15
32
|
}),
|
|
16
33
|
async execute(client, input) {
|
|
17
34
|
const result = await client.listConversations(input);
|
|
35
|
+
const skippedNote = describeSkippedWorkspaces(result);
|
|
18
36
|
if (result.conversations.length === 0) {
|
|
19
|
-
return
|
|
37
|
+
return [
|
|
38
|
+
'No inbox conversations found matching your filters.',
|
|
39
|
+
skippedNote,
|
|
40
|
+
].filter(Boolean).join('\n\n');
|
|
20
41
|
}
|
|
21
42
|
const lines = [`Inbox conversations (showing ${result.conversations.length} of ${result.total}):`];
|
|
22
43
|
for (const conversation of result.conversations) {
|
|
@@ -30,6 +51,8 @@ export const listConversationsTool = {
|
|
|
30
51
|
const nextOffset = (input.offset ?? 0) + result.conversations.length;
|
|
31
52
|
lines.push('', `More conversations available. Call again with offset: ${nextOffset}.`);
|
|
32
53
|
}
|
|
54
|
+
if (skippedNote)
|
|
55
|
+
lines.push('', skippedNote);
|
|
33
56
|
return lines.join('\n');
|
|
34
57
|
},
|
|
35
58
|
};
|
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, workspace_id, approval_status, or brand_id. If workspace_id is omitted,
|
|
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, this returns your own posts, across workspaces, not every post in those workspaces.',
|
|
7
7
|
inputSchema: z.object({
|
|
8
8
|
status: z
|
|
9
9
|
.string()
|
|
@@ -6,7 +6,7 @@ export const replyToCommentTool = {
|
|
|
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.'),
|
|
8
8
|
type: z.enum(['public', 'private']).optional().describe('public (default) or private. Private is Instagram only.'),
|
|
9
|
-
workspace_id: z.string().optional().describe('
|
|
9
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
10
10
|
confirm: z.literal(true).describe('Must be true after explicit user confirmation.'),
|
|
11
11
|
}),
|
|
12
12
|
async execute(client, input) {
|
|
@@ -5,7 +5,7 @@ export const sendMessageTool = {
|
|
|
5
5
|
inputSchema: z.object({
|
|
6
6
|
conversation_id: z.string().min(1).describe('Conversation ID from list_conversations.'),
|
|
7
7
|
content: z.string().min(1).describe('Message text to send.'),
|
|
8
|
-
workspace_id: z.string().optional().describe('
|
|
8
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
9
9
|
confirm: z.literal(true).describe('Must be true after explicit user confirmation.'),
|
|
10
10
|
}),
|
|
11
11
|
async execute(client, input) {
|
package/dist/tools/sync-inbox.js
CHANGED
|
@@ -5,7 +5,7 @@ export const syncInboxTool = {
|
|
|
5
5
|
inputSchema: z.object({
|
|
6
6
|
account_id: z.number().int().positive().describe('Social account ID from list_accounts.'),
|
|
7
7
|
sync_type: z.enum(['all', 'dm', 'comments']).optional().describe('What to sync. Defaults to all. Threads and LinkedIn are comments-only.'),
|
|
8
|
-
workspace_id: z.string().optional().describe('
|
|
8
|
+
workspace_id: z.string().optional().describe('Optional. Omit to use the connected account\'s own workspace; pass a workspace ID from whoami to require that specific workspace.'),
|
|
9
9
|
}),
|
|
10
10
|
async execute(client, input) {
|
|
11
11
|
const result = await client.syncInbox(input);
|
|
@@ -6,7 +6,7 @@ export const updateCommentTool = {
|
|
|
6
6
|
comment_id: z.string().min(1).describe('Comment ID from list_comments.'),
|
|
7
7
|
is_hidden: z.boolean().optional().describe('Hide or unhide the comment on Instagram, Facebook, or Threads.'),
|
|
8
8
|
is_read: z.boolean().optional().describe('Mark the comment read or unread in posterly.'),
|
|
9
|
-
workspace_id: z.string().optional().describe('
|
|
9
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
10
10
|
}),
|
|
11
11
|
async execute(client, input) {
|
|
12
12
|
const result = await client.updateComment(input);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "posterly-mcp-server",
|
|
3
|
-
"version": "0.43.
|
|
3
|
+
"version": "0.43.7",
|
|
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.7",
|
|
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.7",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
|
20
20
|
},
|
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.7';
|
|
@@ -7,7 +7,7 @@ export const deleteCommentTool = {
|
|
|
7
7
|
'Delete an Instagram or Facebook Page inbox comment. DESTRUCTIVE and IRREVERSIBLE. Threads replies cannot be deleted; hide them with update_comment instead. Requires a Pro plan or higher, posts:write, and admin workspace access. Fetch with get_comment first and pass confirm=true only after explicit user confirmation.',
|
|
8
8
|
inputSchema: z.object({
|
|
9
9
|
comment_id: z.string().min(1).describe('Comment ID from list_comments or get_comment.'),
|
|
10
|
-
workspace_id: z.string().optional().describe('
|
|
10
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
11
11
|
confirm: z.literal(true).describe('Must be true after explicit user confirmation of the comment deletion.'),
|
|
12
12
|
}),
|
|
13
13
|
|
package/src/tools/get-comment.ts
CHANGED
|
@@ -8,7 +8,7 @@ export const getCommentTool = {
|
|
|
8
8
|
'Get one social inbox comment and its replies. Instagram, Facebook Page, Threads, and LinkedIn Page comments. Requires a Pro plan or higher plus posts:read.',
|
|
9
9
|
inputSchema: z.object({
|
|
10
10
|
comment_id: z.string().min(1).describe('Comment ID from list_comments.'),
|
|
11
|
-
workspace_id: z.string().optional().describe('
|
|
11
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
12
12
|
}),
|
|
13
13
|
|
|
14
14
|
async execute(
|
|
@@ -8,7 +8,7 @@ export const getConversationTool = {
|
|
|
8
8
|
'Get one social inbox DM conversation and its messages. Instagram and Facebook Page DMs only. Requires a Pro plan or higher plus posts:read.',
|
|
9
9
|
inputSchema: z.object({
|
|
10
10
|
conversation_id: z.string().min(1).describe('Conversation ID from list_conversations.'),
|
|
11
|
-
workspace_id: z.string().optional().describe('
|
|
11
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
12
12
|
limit: z.number().int().min(1).max(100).optional().describe('Maximum messages to return. Default 50, max 100.'),
|
|
13
13
|
}),
|
|
14
14
|
|
|
@@ -4,12 +4,29 @@ import { truncateText } from '../lib/format.js';
|
|
|
4
4
|
|
|
5
5
|
const INBOX_PLATFORMS = ['instagram', 'facebook', 'threads', 'linkedin'] as const;
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* The shared `InboxConversationsResponse`/`InboxCommentsResponse` types
|
|
9
|
+
* don't declare `workspaces_skipped` (a plain pass-through field from the
|
|
10
|
+
* API), so it's read here through a loose cast rather than widening those
|
|
11
|
+
* shared types for one caller.
|
|
12
|
+
*/
|
|
13
|
+
function describeSkippedWorkspaces(result: unknown): string | null {
|
|
14
|
+
const skipped = (result as { workspaces_skipped?: Array<{ workspace_id?: string; name?: string; reason?: string }> })?.workspaces_skipped;
|
|
15
|
+
if (!Array.isArray(skipped) || skipped.length === 0) return null;
|
|
16
|
+
const lines = skipped.map((skip) => {
|
|
17
|
+
const name = skip?.name || skip?.workspace_id || 'a workspace';
|
|
18
|
+
const reason = skip?.reason === 'plan' ? "owner's plan does not include inbox" : 'your role there is too low';
|
|
19
|
+
return `- ${name}: ${reason}`;
|
|
20
|
+
});
|
|
21
|
+
return [`Skipped ${skipped.length} workspace${skipped.length === 1 ? '' : 's'} you belong to:`, ...lines].join('\n');
|
|
22
|
+
}
|
|
23
|
+
|
|
7
24
|
export const listCommentsTool = {
|
|
8
25
|
name: 'list_comments',
|
|
9
26
|
description:
|
|
10
27
|
'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.',
|
|
11
28
|
inputSchema: z.object({
|
|
12
|
-
workspace_id: z.string().optional().describe('
|
|
29
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
13
30
|
account_id: z.number().int().positive().optional().describe('Social account ID from list_accounts.'),
|
|
14
31
|
platform: z.enum(INBOX_PLATFORMS).optional().describe('instagram, facebook, threads, or linkedin.'),
|
|
15
32
|
filter: z.enum(['all', 'unread', 'flagged']).optional().describe('Comment filter. Defaults to all.'),
|
|
@@ -31,8 +48,12 @@ export const listCommentsTool = {
|
|
|
31
48
|
},
|
|
32
49
|
) {
|
|
33
50
|
const result = await client.listComments(input);
|
|
51
|
+
const skippedNote = describeSkippedWorkspaces(result);
|
|
34
52
|
if (result.comments.length === 0) {
|
|
35
|
-
return
|
|
53
|
+
return [
|
|
54
|
+
'No inbox comments found matching your filters.',
|
|
55
|
+
skippedNote,
|
|
56
|
+
].filter(Boolean).join('\n\n');
|
|
36
57
|
}
|
|
37
58
|
|
|
38
59
|
const lines = [`Inbox comments (showing ${result.comments.length} of ${result.total}):`];
|
|
@@ -54,6 +75,8 @@ export const listCommentsTool = {
|
|
|
54
75
|
lines.push('', `More comments available. Call again with offset: ${nextOffset}.`);
|
|
55
76
|
}
|
|
56
77
|
|
|
78
|
+
if (skippedNote) lines.push('', skippedNote);
|
|
79
|
+
|
|
57
80
|
return lines.join('\n');
|
|
58
81
|
},
|
|
59
82
|
};
|
|
@@ -4,12 +4,29 @@ import { truncateText } from '../lib/format.js';
|
|
|
4
4
|
|
|
5
5
|
const INBOX_PLATFORMS = ['instagram', 'facebook', 'threads'] as const;
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* The shared `InboxConversationsResponse`/`InboxCommentsResponse` types
|
|
9
|
+
* don't declare `workspaces_skipped` (a plain pass-through field from the
|
|
10
|
+
* API), so it's read here through a loose cast rather than widening those
|
|
11
|
+
* shared types for one caller.
|
|
12
|
+
*/
|
|
13
|
+
function describeSkippedWorkspaces(result: unknown): string | null {
|
|
14
|
+
const skipped = (result as { workspaces_skipped?: Array<{ workspace_id?: string; name?: string; reason?: string }> })?.workspaces_skipped;
|
|
15
|
+
if (!Array.isArray(skipped) || skipped.length === 0) return null;
|
|
16
|
+
const lines = skipped.map((skip) => {
|
|
17
|
+
const name = skip?.name || skip?.workspace_id || 'a workspace';
|
|
18
|
+
const reason = skip?.reason === 'plan' ? "owner's plan does not include inbox" : 'your role there is too low';
|
|
19
|
+
return `- ${name}: ${reason}`;
|
|
20
|
+
});
|
|
21
|
+
return [`Skipped ${skipped.length} workspace${skipped.length === 1 ? '' : 's'} you belong to:`, ...lines].join('\n');
|
|
22
|
+
}
|
|
23
|
+
|
|
7
24
|
export const listConversationsTool = {
|
|
8
25
|
name: 'list_conversations',
|
|
9
26
|
description:
|
|
10
27
|
'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
28
|
inputSchema: z.object({
|
|
12
|
-
workspace_id: z.string().optional().describe('
|
|
29
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
13
30
|
account_id: z.number().int().positive().optional().describe('Social account ID from list_accounts.'),
|
|
14
31
|
platform: z.enum(INBOX_PLATFORMS).optional().describe('instagram or facebook. Threads has no DMs.'),
|
|
15
32
|
filter: z.enum(['all', 'unread', 'starred', 'archived']).optional().describe('Conversation filter. Defaults to all (excludes archived).'),
|
|
@@ -31,8 +48,12 @@ export const listConversationsTool = {
|
|
|
31
48
|
},
|
|
32
49
|
) {
|
|
33
50
|
const result = await client.listConversations(input);
|
|
51
|
+
const skippedNote = describeSkippedWorkspaces(result);
|
|
34
52
|
if (result.conversations.length === 0) {
|
|
35
|
-
return
|
|
53
|
+
return [
|
|
54
|
+
'No inbox conversations found matching your filters.',
|
|
55
|
+
skippedNote,
|
|
56
|
+
].filter(Boolean).join('\n\n');
|
|
36
57
|
}
|
|
37
58
|
|
|
38
59
|
const lines = [`Inbox conversations (showing ${result.conversations.length} of ${result.total}):`];
|
|
@@ -49,6 +70,8 @@ export const listConversationsTool = {
|
|
|
49
70
|
lines.push('', `More conversations available. Call again with offset: ${nextOffset}.`);
|
|
50
71
|
}
|
|
51
72
|
|
|
73
|
+
if (skippedNote) lines.push('', skippedNote);
|
|
74
|
+
|
|
52
75
|
return lines.join('\n');
|
|
53
76
|
},
|
|
54
77
|
};
|
package/src/tools/list-posts.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { SUPPORTED_PLATFORM_INPUTS, SUPPORTED_PLATFORM_IDS } from '../generated/
|
|
|
6
6
|
export const listPostsTool = {
|
|
7
7
|
name: 'list_posts',
|
|
8
8
|
description:
|
|
9
|
-
'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,
|
|
9
|
+
'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, this returns your own posts, across workspaces, not every post in those workspaces.',
|
|
10
10
|
inputSchema: z.object({
|
|
11
11
|
status: z
|
|
12
12
|
.string()
|
|
@@ -9,7 +9,7 @@ export const replyToCommentTool = {
|
|
|
9
9
|
comment_id: z.string().min(1).describe('Comment ID from list_comments.'),
|
|
10
10
|
content: z.string().min(1).describe('Reply text to post.'),
|
|
11
11
|
type: z.enum(['public', 'private']).optional().describe('public (default) or private. Private is Instagram only.'),
|
|
12
|
-
workspace_id: z.string().optional().describe('
|
|
12
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
13
13
|
confirm: z.literal(true).describe('Must be true after explicit user confirmation.'),
|
|
14
14
|
}),
|
|
15
15
|
|
|
@@ -8,7 +8,7 @@ export const sendMessageTool = {
|
|
|
8
8
|
inputSchema: z.object({
|
|
9
9
|
conversation_id: z.string().min(1).describe('Conversation ID from list_conversations.'),
|
|
10
10
|
content: z.string().min(1).describe('Message text to send.'),
|
|
11
|
-
workspace_id: z.string().optional().describe('
|
|
11
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
12
12
|
confirm: z.literal(true).describe('Must be true after explicit user confirmation.'),
|
|
13
13
|
}),
|
|
14
14
|
|
package/src/tools/sync-inbox.ts
CHANGED
|
@@ -8,7 +8,7 @@ export const syncInboxTool = {
|
|
|
8
8
|
inputSchema: z.object({
|
|
9
9
|
account_id: z.number().int().positive().describe('Social account ID from list_accounts.'),
|
|
10
10
|
sync_type: z.enum(['all', 'dm', 'comments']).optional().describe('What to sync. Defaults to all. Threads and LinkedIn are comments-only.'),
|
|
11
|
-
workspace_id: z.string().optional().describe('
|
|
11
|
+
workspace_id: z.string().optional().describe('Optional. Omit to use the connected account\'s own workspace; pass a workspace ID from whoami to require that specific workspace.'),
|
|
12
12
|
}),
|
|
13
13
|
|
|
14
14
|
async execute(
|
|
@@ -9,7 +9,7 @@ export const updateCommentTool = {
|
|
|
9
9
|
comment_id: z.string().min(1).describe('Comment ID from list_comments.'),
|
|
10
10
|
is_hidden: z.boolean().optional().describe('Hide or unhide the comment on Instagram, Facebook, or Threads.'),
|
|
11
11
|
is_read: z.boolean().optional().describe('Mark the comment read or unread in posterly.'),
|
|
12
|
-
workspace_id: z.string().optional().describe('
|
|
12
|
+
workspace_id: z.string().optional().describe('Optional. Omit to search every workspace you belong to; pass a workspace ID from whoami to limit to one.'),
|
|
13
13
|
}),
|
|
14
14
|
|
|
15
15
|
async execute(
|