postfast-mcp 0.1.24 → 0.3.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.
Files changed (64) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +8 -1
  4. package/dist/core/backend-port.d.ts +138 -0
  5. package/dist/core/backend-port.js +2 -0
  6. package/dist/core/backend-port.js.map +1 -0
  7. package/dist/core/build-tools.d.ts +38 -0
  8. package/dist/core/build-tools.js +84 -0
  9. package/dist/core/build-tools.js.map +1 -0
  10. package/dist/core/index.d.ts +12 -0
  11. package/dist/core/index.js +12 -0
  12. package/dist/core/index.js.map +1 -0
  13. package/dist/core/instructions.d.ts +3 -0
  14. package/dist/core/instructions.js +63 -0
  15. package/dist/core/instructions.js.map +1 -0
  16. package/dist/core/shared.d.ts +23 -0
  17. package/dist/core/shared.js +60 -0
  18. package/dist/core/shared.js.map +1 -0
  19. package/dist/core/tool-def.d.ts +55 -0
  20. package/dist/core/tool-def.js +2 -0
  21. package/dist/core/tool-def.js.map +1 -0
  22. package/dist/core/tools/accounts.d.ts +2 -0
  23. package/dist/core/tools/accounts.js +112 -0
  24. package/dist/core/tools/accounts.js.map +1 -0
  25. package/dist/core/tools/inbox.d.ts +11 -0
  26. package/dist/core/tools/inbox.js +191 -0
  27. package/dist/core/tools/inbox.js.map +1 -0
  28. package/dist/core/tools/index.d.ts +8 -0
  29. package/dist/core/tools/index.js +19 -0
  30. package/dist/core/tools/index.js.map +1 -0
  31. package/dist/core/tools/posts.d.ts +2 -0
  32. package/dist/core/tools/posts.js +264 -0
  33. package/dist/core/tools/posts.js.map +1 -0
  34. package/dist/core/tools/uploads.d.ts +8 -0
  35. package/dist/core/tools/uploads.js +102 -0
  36. package/dist/core/tools/uploads.js.map +1 -0
  37. package/dist/core/tools/workspaces.d.ts +2 -0
  38. package/dist/core/tools/workspaces.js +15 -0
  39. package/dist/core/tools/workspaces.js.map +1 -0
  40. package/dist/core/types.js.map +1 -0
  41. package/dist/stdio/index.js +15 -0
  42. package/dist/stdio/index.js.map +1 -0
  43. package/dist/stdio/rest-adapter.d.ts +37 -0
  44. package/dist/stdio/rest-adapter.js +222 -0
  45. package/dist/stdio/rest-adapter.js.map +1 -0
  46. package/package.json +20 -7
  47. package/dist/client.d.ts +0 -9
  48. package/dist/client.js +0 -57
  49. package/dist/client.js.map +0 -1
  50. package/dist/index.js +0 -18
  51. package/dist/index.js.map +0 -1
  52. package/dist/tools/accounts.d.ts +0 -3
  53. package/dist/tools/accounts.js +0 -118
  54. package/dist/tools/accounts.js.map +0 -1
  55. package/dist/tools/files.d.ts +0 -3
  56. package/dist/tools/files.js +0 -87
  57. package/dist/tools/files.js.map +0 -1
  58. package/dist/tools/posts.d.ts +0 -3
  59. package/dist/tools/posts.js +0 -228
  60. package/dist/tools/posts.js.map +0 -1
  61. package/dist/types.js.map +0 -1
  62. /package/dist/{types.d.ts → core/types.d.ts} +0 -0
  63. /package/dist/{types.js → core/types.js} +0 -0
  64. /package/dist/{index.d.ts → stdio/index.d.ts} +0 -0
@@ -0,0 +1,112 @@
1
+ import { z } from 'zod';
2
+ import { dataListOutputSchema } from '../shared.js';
3
+ export const accountTools = [
4
+ {
5
+ name: 'list_accounts',
6
+ binding: 'both',
7
+ title: 'List Accounts',
8
+ description: 'List all social media accounts connected to the workspace. Each account includes connectionStatus (CONNECTED or DISABLED) and disabledReason (null unless DISABLED), plus followerCount (latest stored snapshot, a string; absent for platforms without follower data), followerCountUpdatedAt, and inboxCapable (whether the account can appear in the social inbox, i.e. comment ingestion is supported). A DISABLED account will not publish until the user reconnects it — pre-check before scheduling. For a follower trend over time, use get_follower_history.',
9
+ inputSchema: {},
10
+ outputSchema: dataListOutputSchema,
11
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
12
+ run: (port, _args, workspaceId) => port.listAccounts(workspaceId),
13
+ },
14
+ {
15
+ name: 'list_pinterest_boards',
16
+ binding: 'both',
17
+ title: 'List Pinterest Boards',
18
+ description: 'List Pinterest boards for a connected Pinterest account (pass its socialMediaId). Use a returned board’s boardId field as controls.pinterestBoardId in create_posts.',
19
+ inputSchema: {
20
+ socialMediaId: z.uuid().describe('Pinterest account id (from list_accounts)'),
21
+ },
22
+ outputSchema: dataListOutputSchema,
23
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
24
+ run: (port, args, workspaceId) => port.listPinterestBoards(args.socialMediaId, workspaceId),
25
+ },
26
+ {
27
+ name: 'list_youtube_playlists',
28
+ binding: 'both',
29
+ title: 'List YouTube Playlists',
30
+ description: 'List YouTube playlists for a connected YouTube account (pass its socialMediaId). Use a returned playlist’s playlistId field as controls.youtubePlaylistId in create_posts.',
31
+ inputSchema: {
32
+ socialMediaId: z.uuid().describe('YouTube account id (from list_accounts)'),
33
+ },
34
+ outputSchema: dataListOutputSchema,
35
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
36
+ run: (port, args, workspaceId) => port.listYoutubePlaylists(args.socialMediaId, workspaceId),
37
+ },
38
+ {
39
+ name: 'list_gbp_locations',
40
+ binding: 'both',
41
+ title: 'List Google Business Profile Locations',
42
+ description: 'List Google Business Profile locations for a connected GBP account (pass its socialMediaId). Use a returned location’s locationId field as controls.gbpLocationId in create_posts.',
43
+ inputSchema: {
44
+ socialMediaId: z.uuid().describe('GBP account id (from list_accounts)'),
45
+ },
46
+ outputSchema: dataListOutputSchema,
47
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
48
+ run: (port, args, workspaceId) => port.listGbpLocations(args.socialMediaId, workspaceId),
49
+ },
50
+ {
51
+ name: 'get_follower_history',
52
+ binding: 'both',
53
+ title: 'Get Follower History',
54
+ description: 'Daily follower-count snapshots for one connected account (pass its socialMediaId from list_accounts). Returns currentFollowerCount, delta (current − first snapshot in range), trackingStartedAt (when PostFast began recording this account), and a series of { capturedAt, followerCount } points. All counts are strings (bigint); currentFollowerCount, delta, and trackingStartedAt may be absent until the account has its first snapshot. Optional from/to bound the range (ISO 8601; default last 90 days, capped at 365). Snapshots are forward-only — no data before trackingStartedAt. Coverage: Facebook Pages, Instagram, YouTube, Pinterest, Threads, Bluesky, Telegram, LinkedIn company pages, and TikTok. Not available: X, personal Facebook.',
55
+ inputSchema: {
56
+ socialMediaId: z.uuid().describe('Account id (from list_accounts)'),
57
+ from: z
58
+ .string()
59
+ .optional()
60
+ .describe('Range start (ISO 8601, e.g. 2026-03-01T00:00:00.000Z). Defaults to 90 days ago.'),
61
+ to: z
62
+ .string()
63
+ .optional()
64
+ .describe('Range end (ISO 8601). Defaults to now; range is capped at 365 days.'),
65
+ },
66
+ outputSchema: {
67
+ socialMediaId: z.string().optional(),
68
+ series: z.array(z.unknown()).optional(),
69
+ currentFollowerCount: z.string().optional(),
70
+ delta: z.string().optional(),
71
+ trackingStartedAt: z.string().optional(),
72
+ },
73
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
74
+ run: (port, args, workspaceId) => port.getFollowerHistory(args, workspaceId),
75
+ },
76
+ {
77
+ name: 'search_places',
78
+ binding: 'both',
79
+ title: 'Search Places',
80
+ description: 'Search for a place to geotag a post. Pass free text (min 2 chars, e.g. a venue name or address); returns matching places, each with an id that works as BOTH controls.facebookPlaceId (Facebook feed posts) and controls.instagramLocationId (Instagram single-media posts). Only Facebook Pages that carry location data are returned.',
81
+ inputSchema: {
82
+ query: z.string().min(2).describe('Place search text, min 2 characters (e.g. "eiffel tower")'),
83
+ },
84
+ outputSchema: dataListOutputSchema,
85
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
86
+ run: (port, args, workspaceId) => port.searchPlaces(args.query, workspaceId),
87
+ },
88
+ {
89
+ name: 'generate_connect_link',
90
+ binding: 'both',
91
+ title: 'Generate Connect Link',
92
+ description: 'Generate a shareable link for external clients to connect their social accounts to the workspace.',
93
+ inputSchema: {
94
+ expiryDays: z
95
+ .number()
96
+ .int()
97
+ .min(1)
98
+ .max(30)
99
+ .default(7)
100
+ .describe('Link expiry in days (1-30, default 7)'),
101
+ sendEmail: z.boolean().default(false).describe('Send the link via email'),
102
+ email: z
103
+ .email()
104
+ .optional()
105
+ .describe('Recipient email (required when sendEmail is true)'),
106
+ },
107
+ outputSchema: { connectUrl: z.string().optional() },
108
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
109
+ run: (port, args, workspaceId) => port.generateConnectLink(args, workspaceId),
110
+ },
111
+ ];
112
+ //# sourceMappingURL=accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../../src/core/tools/accounts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ToolDef } from '../tool-def.js';
2
+ /**
3
+ * Social inbox — a COMMENTS inbox (comments on the workspace's own posts;
4
+ * never described as DMs/messages). Coverage: TikTok (Business connections),
5
+ * Instagram, Facebook Pages, Threads. Reply capability is server-computed per
6
+ * conversation (canReply / maxReplyLength / windowState / disabledReason) —
7
+ * descriptions steer models to render from those fields, never platform rules.
8
+ * Every def carries portMethod so bindings whose adapter predates this wave
9
+ * skip these tools instead of shipping broken ones.
10
+ */
11
+ export declare const inboxTools: ToolDef[];
@@ -0,0 +1,191 @@
1
+ import { z } from 'zod';
2
+ import { INBOX_CONVERSATION_STATUSES, INBOX_ITEM_STATE_ACTIONS, PLATFORMS, dataListOutputSchema, } from '../shared.js';
3
+ /**
4
+ * Social inbox — a COMMENTS inbox (comments on the workspace's own posts;
5
+ * never described as DMs/messages). Coverage: TikTok (Business connections),
6
+ * Instagram, Facebook Pages, Threads. Reply capability is server-computed per
7
+ * conversation (canReply / maxReplyLength / windowState / disabledReason) —
8
+ * descriptions steer models to render from those fields, never platform rules.
9
+ * Every def carries portMethod so bindings whose adapter predates this wave
10
+ * skip these tools instead of shipping broken ones.
11
+ */
12
+ export const inboxTools = [
13
+ {
14
+ name: 'list_inbox_conversations',
15
+ binding: 'both',
16
+ title: 'List Inbox Conversations',
17
+ description: "List comment conversations from the social inbox — comments on your connected accounts' posts, grouped per post, newest activity first. Covers TikTok (Business connections), Instagram, Facebook Pages, and Threads; comments arrive within seconds of being posted and only from connect/launch onward (no history backfill). Each conversation carries a server-computed reply capability — canReply, maxReplyLength, windowState, disabledReason — plus unreadCount, status (OPEN | SNOOZED | CLOSED), and assignedToUserId. ALWAYS derive whether and how long you can reply from those fields; never assume platform rules.",
18
+ inputSchema: {
19
+ page: z.number().int().min(0).default(0).describe('Page number (0-based)'),
20
+ limit: z
21
+ .number()
22
+ .int()
23
+ .min(1)
24
+ .max(50)
25
+ .default(20)
26
+ .describe('Conversations per page (max 50)'),
27
+ platforms: z.array(z.enum(PLATFORMS)).optional().describe('Filter by platforms'),
28
+ socialMediaIds: z
29
+ .array(z.uuid())
30
+ .optional()
31
+ .describe('Filter by specific social media account ids (from list_accounts)'),
32
+ statuses: z
33
+ .array(z.enum(INBOX_CONVERSATION_STATUSES))
34
+ .optional()
35
+ .describe('Filter by conversation statuses'),
36
+ unreadOnly: z
37
+ .boolean()
38
+ .optional()
39
+ .describe('Only conversations with unread comments'),
40
+ assignedToUserId: z
41
+ .uuid()
42
+ .optional()
43
+ .describe('Only conversations assigned to this workspace member'),
44
+ },
45
+ outputSchema: dataListOutputSchema,
46
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
47
+ portMethod: 'listInboxConversations',
48
+ run: (port, args, workspaceId) => port.listInboxConversations(args, workspaceId),
49
+ },
50
+ {
51
+ name: 'get_inbox_conversation',
52
+ binding: 'both',
53
+ title: 'Get Inbox Conversation',
54
+ description: 'Fetch one inbox conversation by id, including its server-computed reply capability (canReply, maxReplyLength, windowState, disabledReason) — derive reply ability from these fields, never from hardcoded platform rules. Returns null when the conversation does not exist in the workspace.',
55
+ inputSchema: {
56
+ id: z.uuid().describe('Conversation id (from list_inbox_conversations)'),
57
+ },
58
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
59
+ portMethod: 'getInboxConversation',
60
+ run: (port, args, workspaceId) => port.getInboxConversation(args.id, workspaceId),
61
+ },
62
+ {
63
+ name: 'list_inbox_items',
64
+ binding: 'both',
65
+ title: 'List Inbox Items',
66
+ description: 'List the items of one inbox conversation — the comments and the replies sent to them — oldest first by default (order=DESC for newest first). Items carry direction (INBOUND | OUTBOUND), state (VISIBLE | HIDDEN | DELETED), author info, and on Instagram comments canPrivateReply (eligibility for send_inbox_private_reply). Replies sent from PostFast appear exactly once — no duplicates when the platform reports them back.',
67
+ inputSchema: {
68
+ conversationId: z
69
+ .uuid()
70
+ .describe('Conversation id (from list_inbox_conversations)'),
71
+ page: z.number().int().min(0).default(0).describe('Page number (0-based)'),
72
+ limit: z.number().int().min(1).max(50).default(20).describe('Items per page (max 50)'),
73
+ order: z
74
+ .enum(['ASC', 'DESC'])
75
+ .optional()
76
+ .describe('Sort by comment time. Default ASC (oldest first); DESC for newest first.'),
77
+ },
78
+ outputSchema: dataListOutputSchema,
79
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
80
+ portMethod: 'listInboxItems',
81
+ run: (port, args, workspaceId) => port.listInboxItems(args, workspaceId),
82
+ },
83
+ {
84
+ name: 'get_inbox_unread_count',
85
+ binding: 'both',
86
+ title: 'Get Inbox Unread Count',
87
+ description: 'Total unread comment count across all inbox conversations in the workspace (the sum of per-conversation unreadCount). Use mark_inbox_conversation_read after presenting a conversation to the user.',
88
+ inputSchema: {},
89
+ outputSchema: { unreadCount: z.number().optional() },
90
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
91
+ portMethod: 'getInboxUnreadCount',
92
+ run: (port, _args, workspaceId) => port.getInboxUnreadCount(workspaceId),
93
+ },
94
+ {
95
+ name: 'reply_to_inbox_item',
96
+ binding: 'both',
97
+ title: 'Reply to Inbox Comment',
98
+ description: "Reply publicly UNDER a specific comment — pass the comment item's id (from list_inbox_items), not the conversation id. BEFORE replying, check the conversation's canReply and maxReplyLength and stay within them; the caps are per platform (TikTok 150, Instagram 2,200, Facebook 8,000, Threads 500 characters) but the server-computed fields are authoritative — never assume. Failures return inbox.* codes (e.g. replyTooLong, replyNotSupported, rateLimited).",
99
+ inputSchema: {
100
+ itemId: z.uuid().describe('The comment item id to reply under (from list_inbox_items)'),
101
+ text: z.string().min(1).describe("Reply text. Must fit the conversation's maxReplyLength."),
102
+ },
103
+ outputSchema: { id: z.string().optional() },
104
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
105
+ portMethod: 'replyToInboxItem',
106
+ run: (port, args, workspaceId) => port.replyToInboxItem(args, workspaceId),
107
+ },
108
+ {
109
+ name: 'send_inbox_private_reply',
110
+ binding: 'both',
111
+ title: 'Send Instagram Private Reply',
112
+ description: "Instagram only: send ONE private reply to a comment — it arrives as a direct message to the commenter and may land in their Message Requests folder. Allowed once per comment, within 7 days of the comment, up to 1,000 bytes (emoji and non-Latin text count multi-byte — roughly 1,000 characters, less with emoji). Check the item's canPrivateReply first (from list_inbox_items). A second attempt on the same comment fails with inbox.privateReplyAlreadySent; other failures include privateReplyWindowExpired and privateReplyNotSupported.",
113
+ inputSchema: {
114
+ itemId: z
115
+ .uuid()
116
+ .describe('The Instagram comment item id to reply privately to (canPrivateReply must be true)'),
117
+ text: z.string().min(1).describe('Private reply text (max 1,000 bytes)'),
118
+ },
119
+ outputSchema: { id: z.string().optional() },
120
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
121
+ portMethod: 'sendInboxPrivateReply',
122
+ run: (port, args, workspaceId) => port.sendInboxPrivateReply(args, workspaceId),
123
+ },
124
+ {
125
+ name: 'set_inbox_item_state',
126
+ binding: 'both',
127
+ title: 'Moderate Inbox Comment',
128
+ description: 'Moderate a comment on the platform: HIDE hides it from the public, UNHIDE restores it, DELETE removes the comment on the platform — cannot be undone. Hide/unhide work on TikTok, Instagram, Facebook, and Threads; DELETE is not supported on Threads (inbox.deleteNotSupported). State changes made on the platform itself sync back to the inbox automatically.',
129
+ inputSchema: {
130
+ itemId: z.uuid().describe('The comment item id (from list_inbox_items)'),
131
+ action: z
132
+ .enum(INBOX_ITEM_STATE_ACTIONS)
133
+ .describe('HIDE, UNHIDE, or DELETE (DELETE is irreversible)'),
134
+ },
135
+ outputSchema: { id: z.string().optional() },
136
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
137
+ portMethod: 'setInboxItemState',
138
+ run: (port, args, workspaceId) => port.setInboxItemState(args, workspaceId),
139
+ },
140
+ {
141
+ name: 'mark_inbox_conversation_read',
142
+ binding: 'both',
143
+ title: 'Mark Inbox Conversation Read',
144
+ description: "Mark one conversation read (zeroes its unreadCount). Do this after presenting a conversation's comments to the user. Internal to PostFast — nothing changes on the platform.",
145
+ inputSchema: {
146
+ conversationId: z
147
+ .uuid()
148
+ .describe('Conversation id (from list_inbox_conversations)'),
149
+ },
150
+ outputSchema: { id: z.string().optional() },
151
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
152
+ portMethod: 'markInboxConversationRead',
153
+ run: (port, args, workspaceId) => port.markInboxConversationRead(args.conversationId, workspaceId),
154
+ },
155
+ {
156
+ name: 'set_inbox_conversation_status',
157
+ binding: 'both',
158
+ title: 'Set Inbox Conversation Status',
159
+ description: 'Triage a conversation: set its status to OPEN, SNOOZED, or CLOSED. Internal to PostFast — nothing changes on the platform.',
160
+ inputSchema: {
161
+ conversationId: z
162
+ .uuid()
163
+ .describe('Conversation id (from list_inbox_conversations)'),
164
+ status: z.enum(INBOX_CONVERSATION_STATUSES).describe('New conversation status'),
165
+ },
166
+ outputSchema: { id: z.string().optional() },
167
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
168
+ portMethod: 'setInboxConversationStatus',
169
+ run: (port, args, workspaceId) => port.setInboxConversationStatus(args, workspaceId),
170
+ },
171
+ {
172
+ name: 'assign_inbox_conversation',
173
+ binding: 'both',
174
+ title: 'Assign Inbox Conversation',
175
+ description: 'Assign a conversation to a workspace member for follow-up, or omit assigneeUserId to unassign. The assignee must be a member of the workspace (inbox.assigneeNotMember otherwise). Internal to PostFast — nothing changes on the platform.',
176
+ inputSchema: {
177
+ conversationId: z
178
+ .uuid()
179
+ .describe('Conversation id (from list_inbox_conversations)'),
180
+ assigneeUserId: z
181
+ .uuid()
182
+ .optional()
183
+ .describe('Workspace member user id to assign. Omit to unassign.'),
184
+ },
185
+ outputSchema: { id: z.string().optional() },
186
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
187
+ portMethod: 'assignInboxConversation',
188
+ run: (port, args, workspaceId) => port.assignInboxConversation(args, workspaceId),
189
+ },
190
+ ];
191
+ //# sourceMappingURL=inbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbox.js","sourceRoot":"","sources":["../../../src/core/tools/inbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ToolDef } from '../tool-def.js';
2
+ /**
3
+ * Every PostFast tool, in registration order. The order filtered to a binding
4
+ * must stay a superset-append of the published order — clients and their
5
+ * prompts see it, so existing tools keep their positions and new waves go
6
+ * at the end.
7
+ */
8
+ export declare const ALL_TOOLS: ToolDef[];
@@ -0,0 +1,19 @@
1
+ import { accountTools } from './accounts.js';
2
+ import { inboxTools } from './inbox.js';
3
+ import { postTools } from './posts.js';
4
+ import { uploadTools } from './uploads.js';
5
+ import { workspaceTools } from './workspaces.js';
6
+ /**
7
+ * Every PostFast tool, in registration order. The order filtered to a binding
8
+ * must stay a superset-append of the published order — clients and their
9
+ * prompts see it, so existing tools keep their positions and new waves go
10
+ * at the end.
11
+ */
12
+ export const ALL_TOOLS = [
13
+ ...postTools,
14
+ ...accountTools,
15
+ ...workspaceTools,
16
+ ...uploadTools,
17
+ ...inboxTools,
18
+ ];
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAc;IAClC,GAAG,SAAS;IACZ,GAAG,YAAY;IACf,GAAG,cAAc;IACjB,GAAG,WAAW;IACd,GAAG,UAAU;CACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ToolDef } from '../tool-def.js';
2
+ export declare const postTools: ToolDef[];
@@ -0,0 +1,264 @@
1
+ import { z } from 'zod';
2
+ import { CREATE_APPROVAL_STATUSES, CREATE_STATUSES, PLATFORMS, POST_STATUSES, SET_APPROVAL_STATUSES, dataListOutputSchema, jsonParse, } from '../shared.js';
3
+ /** The upload tools available on each binding, for prose that names them. */
4
+ const UPLOAD_TOOLS = {
5
+ stdio: 'upload_media / get_upload_urls',
6
+ remote: 'upload_from_url / upload_media',
7
+ };
8
+ const mediaItemSchema = (binding) => z.object({
9
+ key: z
10
+ .string()
11
+ .describe(`Media key from ${UPLOAD_TOOLS[binding]} (e.g. image/<uuid>.jpg)`),
12
+ type: z.enum(['IMAGE', 'VIDEO']),
13
+ sortOrder: z.number().int().min(0),
14
+ coverImageKey: z
15
+ .string()
16
+ .optional()
17
+ .describe('Media key of a custom cover/thumbnail for video posts. Supported on Instagram Reels, Facebook Reels, Pinterest video pins.'),
18
+ coverTimestamp: z
19
+ .string()
20
+ .optional()
21
+ .describe('Video cover timestamp in milliseconds (e.g. "5000" = 5s). Fallback when coverImageKey is also provided.'),
22
+ });
23
+ const postItemSchema = (binding) => z.object({
24
+ content: z.string().describe('Post text content'),
25
+ firstComment: z
26
+ .string()
27
+ .optional()
28
+ .describe('First comment posted automatically after publishing. Supported on X, Instagram, Facebook, YouTube, Threads, and TikTok (TikTok: Business accounts only, max 150 chars, comments must be enabled).'),
29
+ mediaItems: z
30
+ .array(mediaItemSchema(binding))
31
+ .optional()
32
+ .describe('Media attachments'),
33
+ scheduledAt: z
34
+ .string()
35
+ .optional()
36
+ .describe('Schedule time (ISO 8601). Required when status is SCHEDULED.'),
37
+ socialMediaId: z.uuid().describe('Target social account id (from list_accounts)'),
38
+ });
39
+ /** Platform-specific controls — shared across every post in the batch. */
40
+ const controlsSchema = z.object({
41
+ // X/Twitter
42
+ xRetweetUrl: z.string().optional(),
43
+ // TikTok
44
+ tiktokPrivacy: z
45
+ .enum(['PUBLIC', 'MUTUAL_FRIENDS', 'FOLLOWER_OF_CREATOR', 'ONLY_ME'])
46
+ .optional()
47
+ .describe('Deprecated. TikTok videos use the account default privacy (no per-post control) and photos default to public; use a draft for private posts.'),
48
+ tiktokIsDraft: z.boolean().optional(),
49
+ tiktokAllowComments: z.boolean().optional(),
50
+ tiktokAllowDuet: z.boolean().optional(),
51
+ tiktokAllowStitch: z.boolean().optional(),
52
+ tiktokBrandOrganic: z.boolean().optional(),
53
+ tiktokBrandContent: z.boolean().optional(),
54
+ tiktokAutoAddMusic: z.boolean().optional(),
55
+ tiktokIsAigc: z.boolean().optional().describe('Declare video as AI-generated content'),
56
+ tiktokTitle: z
57
+ .string()
58
+ .max(90)
59
+ .optional()
60
+ .describe('Title for TikTok photo posts (max 90 chars; photo posts only). When set, the full post content becomes the description; without it, content auto-splits on the first newline into title + description.'),
61
+ // Instagram
62
+ instagramPostToGrid: z.boolean().optional(),
63
+ instagramPublishType: z.enum(['TIMELINE', 'STORY', 'REEL']).optional(),
64
+ instagramCollaborators: z.array(z.string()).optional(),
65
+ instagramLocationId: z
66
+ .string()
67
+ .optional()
68
+ .describe('Geotag a single-media IG post — a numeric Facebook Page id with location data from search_places (same id as facebookPlaceId). Image/video/reel/story only, NOT carousels.'),
69
+ instagramLocationName: z
70
+ .string()
71
+ .max(255)
72
+ .optional()
73
+ .describe('Display-only place label from search_places (stored for your portal; never sent to Meta)'),
74
+ // YouTube
75
+ youtubePrivacy: z.enum(['PUBLIC', 'PRIVATE', 'UNLISTED']).optional(),
76
+ youtubeTags: z.array(z.string()).optional(),
77
+ youtubeCategoryId: z.string().optional(),
78
+ youtubeIsShort: z.boolean().optional(),
79
+ youtubeMadeForKids: z.boolean().optional(),
80
+ youtubeTitle: z.string().optional(),
81
+ youtubePlaylistId: z
82
+ .string()
83
+ .optional()
84
+ .describe('YouTube playlist id — the playlistId field from list_youtube_playlists'),
85
+ youtubeThumbnailKey: z
86
+ .string()
87
+ .optional()
88
+ .describe('Media key for a custom YouTube thumbnail (image, max 2MB, min 640px wide, 1280x720 recommended)'),
89
+ // Facebook
90
+ facebookContentType: z.enum(['POST', 'REEL', 'STORY']).optional(),
91
+ facebookAllowComments: z.boolean().optional(),
92
+ facebookPrivacy: z
93
+ .enum(['PUBLIC', 'FRIENDS_OF_FRIENDS', 'FRIENDS', 'SELF'])
94
+ .optional(),
95
+ facebookCarouselMainLink: z.string().optional(),
96
+ facebookCarouselShowEndCard: z.boolean().optional(),
97
+ facebookReelsCollaborators: z.array(z.string()).optional(),
98
+ facebookTargetCountries: z
99
+ .array(z.string().length(2))
100
+ .max(25)
101
+ .optional()
102
+ .describe('Limit who can see a Facebook FEED post by country — ISO 3166-1 alpha-2 codes (case-insensitive), max 25. Audience gating (hidden from everyone else), feed posts only — not Reels/Stories/video.'),
103
+ facebookPlaceId: z
104
+ .string()
105
+ .optional()
106
+ .describe('Geotag a Facebook FEED post — a numeric Facebook Page id with location data from search_places (same id as instagramLocationId). Feed posts only (text/photo/carousel) — not Reels/Stories/video.'),
107
+ facebookPlaceName: z
108
+ .string()
109
+ .max(255)
110
+ .optional()
111
+ .describe('Display-only place label from search_places (stored for your portal; never sent to Meta)'),
112
+ // Google Business Profile
113
+ gbpLocationId: z
114
+ .string()
115
+ .optional()
116
+ .describe('GBP location resource name — the locationId field from list_gbp_locations'),
117
+ gbpTopicType: z.enum(['STANDARD', 'EVENT', 'OFFER']).optional().describe('Post type'),
118
+ gbpCallToActionType: z
119
+ .enum(['BOOK', 'ORDER', 'LEARN_MORE', 'SIGN_UP', 'CALL', 'SHOP'])
120
+ .optional(),
121
+ gbpCallToActionUrl: z
122
+ .string()
123
+ .optional()
124
+ .describe('CTA button URL (not needed for CALL, ignored for OFFER)'),
125
+ gbpEventTitle: z.string().optional().describe('Title for EVENT/OFFER posts (max 58 chars)'),
126
+ gbpEventStartDate: z.string().optional().describe('Start date for EVENT/OFFER (ISO 8601)'),
127
+ gbpEventEndDate: z.string().optional().describe('End date for EVENT/OFFER (ISO 8601)'),
128
+ gbpOfferCouponCode: z.string().optional().describe('Coupon code (OFFER only)'),
129
+ gbpOfferRedeemUrl: z.string().optional().describe('Redemption URL (OFFER only)'),
130
+ gbpOfferTerms: z.string().optional().describe('Terms and conditions (OFFER only)'),
131
+ // Pinterest
132
+ pinterestBoardId: z
133
+ .string()
134
+ .optional()
135
+ .describe("Pinterest board id — the boardId field from list_pinterest_boards (NOT the account's socialMediaId)"),
136
+ pinterestLink: z.string().optional(),
137
+ // LinkedIn
138
+ linkedinAttachmentKey: z.string().optional(),
139
+ linkedinAttachmentTitle: z.string().optional(),
140
+ });
141
+ export const postTools = [
142
+ {
143
+ name: 'list_posts',
144
+ binding: 'both',
145
+ title: 'List Posts',
146
+ description: 'List social media posts with optional filters for specific IDs, platform, status, and date range. Failed or missed posts carry a lastError { message, code }; codes include MISSED_DISCONNECTED (the account was disconnected when the post was due — reconnect, then retry) and MISSED_NOT_PUBLISHED (passed its scheduled time plus a 2h grace window without publishing).',
147
+ inputSchema: {
148
+ page: z.number().int().min(0).default(0).describe('Page number (0-based)'),
149
+ limit: z
150
+ .number()
151
+ .int()
152
+ .min(1)
153
+ .max(50)
154
+ .default(20)
155
+ .describe('Posts per page (max 50)'),
156
+ ids: z
157
+ .array(z.uuid())
158
+ .max(100)
159
+ .optional()
160
+ .describe('Fetch only these post ids (workspace-scoped; max 100; AND-ed with other filters)'),
161
+ platforms: z.array(z.enum(PLATFORMS)).optional().describe('Filter by platforms'),
162
+ statuses: z
163
+ .array(z.enum(POST_STATUSES))
164
+ .optional()
165
+ .describe('Filter by post statuses'),
166
+ from: z
167
+ .string()
168
+ .optional()
169
+ .describe('Start date filter (ISO 8601, e.g. 2026-01-01T00:00:00.000Z)'),
170
+ to: z
171
+ .string()
172
+ .optional()
173
+ .describe('End date filter (ISO 8601, e.g. 2026-01-31T23:59:59.999Z)'),
174
+ },
175
+ outputSchema: {
176
+ data: z.array(z.unknown()).optional(),
177
+ totalCount: z.unknown().optional(),
178
+ pageInfo: z.unknown().optional(),
179
+ },
180
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
181
+ run: (port, args, workspaceId) => port.listPosts(args, workspaceId),
182
+ },
183
+ {
184
+ name: 'create_posts',
185
+ binding: 'both',
186
+ title: 'Create Posts',
187
+ description: (binding) => 'Create and schedule social media posts (batch up to 15). Each post targets one social account (socialMediaId from list_accounts). SCHEDULED requires scheduledAt on every post; DRAFT must omit scheduledAt. Scheduling to a disconnected account (connectionStatus DISABLED in list_accounts) is rejected with HTTP 400 "socialMediaDisconnected" — pre-check connectionStatus before calling. Saving as DRAFT to a disconnected account is allowed. TikTok, Instagram, YouTube, Pinterest, and Google Business Profile require at least one media item EVEN FOR DRAFTS — attach media first (ask the user for an image/video, or generate+upload one, if none was provided). Attach media via the key returned by ' +
188
+ `${UPLOAD_TOOLS[binding]}.`,
189
+ inputSchema: (binding) => ({
190
+ posts: jsonParse(z
191
+ .array(postItemSchema(binding))
192
+ .min(1)
193
+ .max(15)
194
+ .describe('Array of posts to create (1-15)')),
195
+ status: z
196
+ .enum(CREATE_STATUSES)
197
+ .default('SCHEDULED')
198
+ .describe('Post status. SCHEDULED requires scheduledAt on every post.'),
199
+ approvalStatus: z
200
+ .enum(CREATE_APPROVAL_STATUSES)
201
+ .default('APPROVED')
202
+ .describe('Approval workflow status'),
203
+ controls: jsonParse(controlsSchema
204
+ .optional()
205
+ .describe('Platform-specific controls (shared across all posts in the batch)')),
206
+ }),
207
+ outputSchema: { postIds: z.array(z.string()).optional() },
208
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
209
+ run: (port, args, workspaceId) => port.createPosts(args, workspaceId),
210
+ },
211
+ {
212
+ name: 'approve_posts',
213
+ binding: 'remote',
214
+ title: 'Approve Posts',
215
+ description: 'Set the approval status of one or more posts (approval workflow). Typically used to move posts to APPROVED so they can publish, or to PENDING_APPROVAL / REJECTED / NEEDS_WORK.',
216
+ inputSchema: {
217
+ postIds: z.array(z.uuid()).min(1).describe('Post ids to update'),
218
+ approvalStatus: z.enum(SET_APPROVAL_STATUSES).describe('New approval status'),
219
+ },
220
+ outputSchema: { success: z.boolean().optional() },
221
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
222
+ run: (port, args, workspaceId) => port.approvePosts(args, workspaceId),
223
+ },
224
+ {
225
+ name: 'delete_post',
226
+ binding: 'both',
227
+ title: 'Delete Post',
228
+ description: 'Delete a social media post by id.',
229
+ inputSchema: {
230
+ id: z.uuid().describe('Post id to delete'),
231
+ },
232
+ outputSchema: { deleted: z.boolean().optional() },
233
+ annotations: {
234
+ readOnlyHint: false,
235
+ destructiveHint: true,
236
+ idempotentHint: true,
237
+ openWorldHint: true,
238
+ },
239
+ run: (port, args, workspaceId) => port.deletePost(args.id, workspaceId),
240
+ },
241
+ {
242
+ name: 'get_post_analytics',
243
+ binding: 'both',
244
+ title: 'Get Post Analytics',
245
+ description: 'Fetch published posts with their latest performance metrics (impressions, reach, likes, comments, shares). Only returns published posts that have a platform post ID. LinkedIn personal accounts are excluded. Supported: Instagram, Facebook, TikTok, Threads, YouTube, LinkedIn (company pages), Pinterest (Business accounts). Metric counts are returned as strings. Pinterest extras include pin_clicks, outbound_clicks, saves_90d, save_rate_90d (saves are 90-day rolling because Pinterest API does not expose lifetime totals); video pins additionally surface mrc_views, views_10s, avg_watch_time, v50_watch_time, video_starts, quartile_95_views. Video posts also include normalized watch-time on latestMetric: avgWatchTimeSeconds, totalWatchTimeSeconds, videoViews (Facebook, Instagram Reels, YouTube, Pinterest, LinkedIn company pages, TikTok). Instagram posts also include on latestMetric: saveRate (saves / reach as a percentage, rounded to 2 decimals — on IG feed posts, reels, and carousels with metrics) and reelsSkipRate (Instagram Reels only — percentage of viewers who skipped the reel in the first 3 seconds, as reported by Instagram, rounded to 2 decimals; may be absent on low-view reels or until metrics refresh). TikTok also exposes total_time_watched, average_time_watched, and full_video_watched_rate in metric.extras.',
246
+ inputSchema: {
247
+ startDate: z
248
+ .string()
249
+ .describe('Start of date range (ISO 8601, e.g. 2026-01-01T00:00:00.000Z)'),
250
+ endDate: z
251
+ .string()
252
+ .describe('End of date range (ISO 8601, e.g. 2026-01-31T23:59:59.999Z)'),
253
+ platforms: z.array(z.enum(PLATFORMS)).optional().describe('Filter by platforms'),
254
+ socialMediaIds: z
255
+ .array(z.uuid())
256
+ .optional()
257
+ .describe('Filter by specific social media account ids'),
258
+ },
259
+ outputSchema: dataListOutputSchema,
260
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
261
+ run: (port, args, workspaceId) => port.getPostAnalytics(args, workspaceId),
262
+ },
263
+ ];
264
+ //# sourceMappingURL=posts.js.map