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
@@ -11,7 +11,7 @@
11
11
  {
12
12
  "name": "postfast",
13
13
  "description": "Schedule, manage, and analyze social media posts via PostFast. Supports Facebook, Instagram, X, TikTok, LinkedIn, YouTube, BlueSky, Threads, Pinterest, Telegram, and Google Business Profile.",
14
- "version": "0.1.24",
14
+ "version": "0.3.0",
15
15
  "author": {
16
16
  "name": "PostFast",
17
17
  "email": "me@peturgeorgievv.com"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "postfast",
3
3
  "description": "Schedule, manage, and analyze social media posts via PostFast. Supports Facebook, Instagram, X, TikTok, LinkedIn, YouTube, BlueSky, Threads, Pinterest, Telegram, and Google Business Profile. After installing, tell Cowork: 'Set my PostFast API key' — get your key at https://app.postfa.st/dashboard → API.",
4
- "version": "0.1.24",
4
+ "version": "0.3.0",
5
5
  "author": {
6
6
  "name": "PostFast",
7
7
  "email": "me@peturgeorgievv.com"
package/README.md CHANGED
@@ -189,9 +189,16 @@ Full REST API documentation: [postfa.st/docs](https://postfa.st/docs)
189
189
  ```bash
190
190
  npm install
191
191
  npm run build
192
- node dist/index.js
192
+ node dist/stdio/index.js
193
193
  ```
194
194
 
195
+ Tools are authored once in `src/core` (the catalog — schemas, descriptions,
196
+ annotations, per-binding availability) and exposed to consumers as
197
+ `postfast-mcp/core`; `src/stdio` binds the catalog to the public REST API.
198
+ Releases: PRs add a `.changeset/*.md`; `npm run version` folds them into the
199
+ bump + changelog, and pushing the `vX.Y.Z` tag publishes npm + MCP Registry +
200
+ the MCPB release from CI (OIDC — no tokens anywhere).
201
+
195
202
  ## Badges
196
203
 
197
204
  [![peturgeorgievv-factory/postfast-mcp MCP server](https://glama.ai/mcp/servers/peturgeorgievv-factory/postfast-mcp/badges/score.svg)](https://glama.ai/mcp/servers/peturgeorgievv-factory/postfast-mcp)
@@ -0,0 +1,138 @@
1
+ import type { AnalyticsResponse, FollowerHistory, GbpLocation, PaginatedPosts, PinterestBoard, Place, SignedUploadUrl, SocialAccount, YouTubePlaylist } from './types.js';
2
+ export interface ListPostsArgs {
3
+ page: number;
4
+ limit: number;
5
+ ids?: string[];
6
+ platforms?: string[];
7
+ statuses?: string[];
8
+ from?: string;
9
+ to?: string;
10
+ }
11
+ export interface CreatePostsArgs {
12
+ posts: unknown[];
13
+ status: string;
14
+ approvalStatus: string;
15
+ controls?: Record<string, unknown>;
16
+ }
17
+ export interface ApprovePostsArgs {
18
+ postIds: string[];
19
+ approvalStatus: string;
20
+ }
21
+ export interface AnalyticsArgs {
22
+ startDate: string;
23
+ endDate: string;
24
+ platforms?: string[];
25
+ socialMediaIds?: string[];
26
+ }
27
+ export interface FollowerHistoryArgs {
28
+ socialMediaId: string;
29
+ from?: string;
30
+ to?: string;
31
+ }
32
+ export interface ConnectLinkArgs {
33
+ expiryDays: number;
34
+ sendEmail: boolean;
35
+ email?: string;
36
+ }
37
+ export interface UploadUrlsArgs {
38
+ contentType: string;
39
+ count: number;
40
+ }
41
+ export interface ListInboxConversationsArgs {
42
+ page: number;
43
+ limit: number;
44
+ platforms?: string[];
45
+ socialMediaIds?: string[];
46
+ statuses?: string[];
47
+ unreadOnly?: boolean;
48
+ assignedToUserId?: string;
49
+ }
50
+ export interface ListInboxItemsArgs {
51
+ conversationId: string;
52
+ page: number;
53
+ limit: number;
54
+ order?: 'ASC' | 'DESC';
55
+ }
56
+ export interface InboxReplyArgs {
57
+ itemId: string;
58
+ text: string;
59
+ }
60
+ export interface SetInboxItemStateArgs {
61
+ itemId: string;
62
+ action: string;
63
+ }
64
+ export interface SetInboxConversationStatusArgs {
65
+ conversationId: string;
66
+ status: string;
67
+ }
68
+ export interface AssignInboxConversationArgs {
69
+ conversationId: string;
70
+ assigneeUserId?: string;
71
+ }
72
+ export interface UploadFromUrlArgs {
73
+ sourceUrl: string;
74
+ contentType?: string;
75
+ }
76
+ /** Remote conversation-media upload: a client-provided file ref or base64 bytes. */
77
+ export interface UploadMediaArgs {
78
+ file?: {
79
+ download_url: string;
80
+ file_id: string;
81
+ mime_type?: string;
82
+ file_name?: string;
83
+ };
84
+ data?: string;
85
+ contentType?: string;
86
+ }
87
+ export interface LocalUploadResult {
88
+ key: string;
89
+ type: 'IMAGE' | 'VIDEO';
90
+ contentType: string;
91
+ }
92
+ /**
93
+ * Everything the catalog tools need from a backend. The stdio bin implements
94
+ * it over the public REST API (pf-api-key); the deployed host implements it
95
+ * over its command gateway + upload service. `workspaceId` is only ever set on
96
+ * the remote binding — adapters without workspace switching ignore it.
97
+ *
98
+ * Adapters may throw from methods whose tools they never register
99
+ * (e.g. the REST adapter for the remote-only conversation-media uploads).
100
+ */
101
+ export interface BackendPort {
102
+ listWorkspaces(): Promise<unknown>;
103
+ listAccounts(workspaceId?: string): Promise<SocialAccount[] | unknown>;
104
+ getFollowerHistory(args: FollowerHistoryArgs, workspaceId?: string): Promise<FollowerHistory | unknown>;
105
+ listPinterestBoards(socialMediaId: string, workspaceId?: string): Promise<PinterestBoard[] | unknown>;
106
+ listYoutubePlaylists(socialMediaId: string, workspaceId?: string): Promise<YouTubePlaylist[] | unknown>;
107
+ listGbpLocations(socialMediaId: string, workspaceId?: string): Promise<GbpLocation[] | unknown>;
108
+ searchPlaces(query: string, workspaceId?: string): Promise<Place[] | unknown>;
109
+ generateConnectLink(args: ConnectLinkArgs, workspaceId?: string): Promise<{
110
+ connectUrl: string;
111
+ } | unknown>;
112
+ listPosts(args: ListPostsArgs, workspaceId?: string): Promise<PaginatedPosts | unknown>;
113
+ createPosts(args: CreatePostsArgs, workspaceId?: string): Promise<{
114
+ postIds: string[];
115
+ } | unknown>;
116
+ approvePosts(args: ApprovePostsArgs, workspaceId?: string): Promise<unknown>;
117
+ deletePost(id: string, workspaceId?: string): Promise<{
118
+ deleted: boolean;
119
+ } | unknown>;
120
+ getPostAnalytics(args: AnalyticsArgs, workspaceId?: string): Promise<AnalyticsResponse | unknown>;
121
+ getUploadUrls(args: UploadUrlsArgs, workspaceId?: string): Promise<SignedUploadUrl[] | unknown>;
122
+ /** stdio only: read a local file and push it through the signed-URL flow. */
123
+ uploadLocalFile(filePath: string): Promise<LocalUploadResult>;
124
+ /** Remote only: server-side fetch of a public https URL (SSRF-guarded in the host). */
125
+ uploadFromUrl(args: UploadFromUrlArgs, workspaceId?: string): Promise<unknown>;
126
+ /** Remote only: upload a conversation-attached file or base64 bytes. */
127
+ uploadMedia(args: UploadMediaArgs, workspaceId?: string): Promise<unknown>;
128
+ listInboxConversations?(args: ListInboxConversationsArgs, workspaceId?: string): Promise<unknown>;
129
+ getInboxConversation?(id: string, workspaceId?: string): Promise<unknown>;
130
+ listInboxItems?(args: ListInboxItemsArgs, workspaceId?: string): Promise<unknown>;
131
+ getInboxUnreadCount?(workspaceId?: string): Promise<unknown>;
132
+ replyToInboxItem?(args: InboxReplyArgs, workspaceId?: string): Promise<unknown>;
133
+ sendInboxPrivateReply?(args: InboxReplyArgs, workspaceId?: string): Promise<unknown>;
134
+ setInboxItemState?(args: SetInboxItemStateArgs, workspaceId?: string): Promise<unknown>;
135
+ markInboxConversationRead?(conversationId: string, workspaceId?: string): Promise<unknown>;
136
+ setInboxConversationStatus?(args: SetInboxConversationStatusArgs, workspaceId?: string): Promise<unknown>;
137
+ assignInboxConversation?(args: AssignInboxConversationArgs, workspaceId?: string): Promise<unknown>;
138
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=backend-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend-port.js","sourceRoot":"","sources":["../../src/core/backend-port.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
3
+ import type { BackendPort } from './backend-port.js';
4
+ import type { Binding, ResolvedTool } from './tool-def.js';
5
+ export interface BuildToolsOptions {
6
+ binding: Binding;
7
+ /**
8
+ * Inject an optional `workspaceId` field into every workspace-scoped tool
9
+ * (the remote host's multi-workspace connections). The stdio bin never sets
10
+ * this — its pf-api-key is already workspace-scoped.
11
+ */
12
+ withWorkspaceField?: boolean;
13
+ /**
14
+ * When given, tools whose `portMethod` the port instance does not implement
15
+ * are skipped with a stderr log — so an adapter built against an older
16
+ * catalog keeps working when a newer catalog adds tools it can't serve yet.
17
+ */
18
+ port?: BackendPort;
19
+ }
20
+ /** Resolve the catalog for one binding: filter, flatten binding-variant fields. */
21
+ export declare function buildTools(options: BuildToolsOptions): ResolvedTool[];
22
+ /**
23
+ * Wrap backend data as MCP content: a JSON text block (proven across all
24
+ * clients — and byte-stable for existing stdio consumers) plus
25
+ * structuredContent for clients that consume outputSchema. Bare-array
26
+ * responses are wrapped as { data } there, since structuredContent must be an
27
+ * object; the text block stays the raw response.
28
+ */
29
+ export declare function toolResult(data: unknown): CallToolResult;
30
+ /** A business/tool error the model should see and react to (not a protocol error). */
31
+ export declare function toolError(message: string): CallToolResult;
32
+ /** Run a handler, converting thrown errors into `isError` tool results. */
33
+ export declare function runTool(fn: () => Promise<unknown>): Promise<CallToolResult>;
34
+ export interface RegisterToolsOptions extends BuildToolsOptions {
35
+ port: BackendPort;
36
+ }
37
+ /** Register the resolved catalog for a binding on an MCP server. */
38
+ export declare function registerCatalogTools(server: McpServer, options: RegisterToolsOptions): void;
@@ -0,0 +1,84 @@
1
+ import { workspaceIdField } from './shared.js';
2
+ import { ALL_TOOLS } from './tools/index.js';
3
+ /** Resolve the catalog for one binding: filter, flatten binding-variant fields. */
4
+ export function buildTools(options) {
5
+ const { binding, withWorkspaceField = false, port } = options;
6
+ return ALL_TOOLS.filter((def) => {
7
+ if (def.binding !== 'both' && def.binding !== binding)
8
+ return false;
9
+ if (port &&
10
+ def.portMethod &&
11
+ typeof port[def.portMethod] !== 'function') {
12
+ console.error(`[postfast-mcp/core] skipping tool "${def.name}": the backend port does not implement ${def.portMethod}() yet (older adapter, newer catalog)`);
13
+ return false;
14
+ }
15
+ return true;
16
+ }).map((def) => {
17
+ const inputSchema = typeof def.inputSchema === 'function' ? def.inputSchema(binding) : def.inputSchema;
18
+ return {
19
+ name: def.name,
20
+ title: def.title,
21
+ description: typeof def.description === 'function' ? def.description(binding) : def.description,
22
+ inputSchema: withWorkspaceField && def.workspaceScoped !== false
23
+ ? { ...inputSchema, workspaceId: workspaceIdField }
24
+ : inputSchema,
25
+ outputSchema: def.outputSchema,
26
+ annotations: def.annotations,
27
+ _meta: def._meta,
28
+ portMethod: def.portMethod,
29
+ run: def.run,
30
+ };
31
+ });
32
+ }
33
+ /**
34
+ * Wrap backend data as MCP content: a JSON text block (proven across all
35
+ * clients — and byte-stable for existing stdio consumers) plus
36
+ * structuredContent for clients that consume outputSchema. Bare-array
37
+ * responses are wrapped as { data } there, since structuredContent must be an
38
+ * object; the text block stays the raw response.
39
+ */
40
+ export function toolResult(data) {
41
+ const result = {
42
+ content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
43
+ };
44
+ if (data && typeof data === 'object') {
45
+ result.structuredContent = Array.isArray(data)
46
+ ? { data }
47
+ : data;
48
+ }
49
+ return result;
50
+ }
51
+ /** A business/tool error the model should see and react to (not a protocol error). */
52
+ export function toolError(message) {
53
+ return {
54
+ content: [{ type: 'text', text: message }],
55
+ isError: true,
56
+ };
57
+ }
58
+ /** Run a handler, converting thrown errors into `isError` tool results. */
59
+ export async function runTool(fn) {
60
+ try {
61
+ return toolResult(await fn());
62
+ }
63
+ catch (err) {
64
+ return toolError(err.message || 'Tool execution failed.');
65
+ }
66
+ }
67
+ /** Register the resolved catalog for a binding on an MCP server. */
68
+ export function registerCatalogTools(server, options) {
69
+ const { port } = options;
70
+ for (const tool of buildTools(options)) {
71
+ server.registerTool(tool.name, {
72
+ title: tool.title,
73
+ description: tool.description,
74
+ inputSchema: tool.inputSchema,
75
+ ...(tool.outputSchema ? { outputSchema: tool.outputSchema } : {}),
76
+ annotations: tool.annotations,
77
+ ...(tool._meta ? { _meta: tool._meta } : {}),
78
+ }, (args) => runTool(() => {
79
+ const { workspaceId, ...rest } = args ?? {};
80
+ return tool.run(port, rest, workspaceId);
81
+ }));
82
+ }
83
+ }
84
+ //# sourceMappingURL=build-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-tools.js","sourceRoot":"","sources":["../../src/core/build-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The PostFast MCP tool catalog — the single place tools are authored.
3
+ * Consumed by two bindings: the stdio bin in this package and the deployed
4
+ * remote host (social-schedule-mcp). Import as "postfast-mcp/core".
5
+ */
6
+ export * from './backend-port.js';
7
+ export { buildTools, registerCatalogTools, runTool, toolError, toolResult, type BuildToolsOptions, type RegisterToolsOptions, } from './build-tools.js';
8
+ export { SERVER_INSTRUCTIONS, instructionsFor } from './instructions.js';
9
+ export { CREATE_APPROVAL_STATUSES, CREATE_STATUSES, IMAGE_MIME_TYPES, PLATFORMS, POST_STATUSES, SET_APPROVAL_STATUSES, VIDEO_MIME_TYPES, dataListOutputSchema, jsonParse, workspaceIdField, } from './shared.js';
10
+ export type { Binding, ResolvedTool, ToolAnnotations, ToolDef, } from './tool-def.js';
11
+ export { ALL_TOOLS } from './tools/index.js';
12
+ export * from './types.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The PostFast MCP tool catalog — the single place tools are authored.
3
+ * Consumed by two bindings: the stdio bin in this package and the deployed
4
+ * remote host (social-schedule-mcp). Import as "postfast-mcp/core".
5
+ */
6
+ export * from './backend-port.js';
7
+ export { buildTools, registerCatalogTools, runTool, toolError, toolResult, } from './build-tools.js';
8
+ export { SERVER_INSTRUCTIONS, instructionsFor } from './instructions.js';
9
+ export { CREATE_APPROVAL_STATUSES, CREATE_STATUSES, IMAGE_MIME_TYPES, PLATFORMS, POST_STATUSES, SET_APPROVAL_STATUSES, VIDEO_MIME_TYPES, dataListOutputSchema, jsonParse, workspaceIdField, } from './shared.js';
10
+ export { ALL_TOOLS } from './tools/index.js';
11
+ export * from './types.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,mBAAmB,CAAC;AAClC,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,UAAU,GAGX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,SAAS,EACT,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAOrB,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,cAAc,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Binding } from './tool-def.js';
2
+ export declare const SERVER_INSTRUCTIONS: Record<Binding, string>;
3
+ export declare function instructionsFor(binding: Binding): string;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Server `instructions` sent at MCP `initialize` — standing guidance the client
3
+ * model reads before calling tools. Grounded in the PostFast docs + the
4
+ * backend's actual create-posts validation, so it matches what the API
5
+ * enforces (prevents the model discovering rules by failing).
6
+ *
7
+ * The two bindings differ only where their tool surfaces differ: workspace
8
+ * switching and conversation-media/URL uploads exist on the remote host, the
9
+ * stdio bin uploads local files instead.
10
+ */
11
+ const SHARED_TAIL = `Before scheduling, confirm the target account's connectionStatus is CONNECTED. A DISABLED account will not publish (disconnected accounts are the #1 cause of failed posts) — saving a DRAFT is still allowed.
12
+
13
+ Status & timing: status=SCHEDULED requires scheduledAt on every post (ISO-8601, in the future, within 1 year); status=DRAFT must omit scheduledAt. approvalStatus=APPROVED publishes; PENDING_APPROVAL holds it for review. There is no instant "publish now" — for an immediate post, set scheduledAt a few minutes ahead of now (a time at or before the current moment is rejected).
14
+
15
+ Media: use the key returned by an upload tool, and set mediaItems[].type to match the file (IMAGE for image/* keys, VIDEO for video/* keys — a mismatch is rejected). Videos are capped at 250MB (Telegram 50MB, Bluesky 100MB). Media is REQUIRED on TikTok, YouTube (video only), Instagram, Pinterest, and Google Business Profile; X, LinkedIn, Facebook, Threads, Bluesky, and Telegram allow text-only posts. This requirement applies even to DRAFTS — a draft to a media-required platform without media is rejected. So if the user asks to post to a media-required platform and provides no media, ask them for an image/video (or generate and upload one) BEFORE calling create_posts; don't attempt the post without media.
16
+
17
+ Per-platform limits (media counts / characters):
18
+ - X: up to 4 images or 1 video (no mixing); 280 chars (4,000 with X Premium).
19
+ - TikTok: 1 video OR up to 10 images; 4,000 chars.
20
+ - Instagram: up to 10 images + 10 videos (mixed OK); 2,200 chars, max 30 hashtags.
21
+ - YouTube: 1 video, no images; title max 100 chars.
22
+ - Facebook / LinkedIn: up to 10 images + 1 video.
23
+ - Threads: up to 10 images + 10 videos; 500 chars.
24
+ - Pinterest: up to 5 images or 1 video; title max 100, description max 800.
25
+ - Google Business Profile: 1 image; 1,500 chars.
26
+ - Bluesky: up to 4 images or 1 video. Telegram: up to 10 images + 3 videos.
27
+ If content exceeds the target platform's character limit, don't schedule it as-is — tell the user and offer to shorten or split it (X is 280 unless the account has X Premium = 4,000).
28
+
29
+ Platform-specific options go in the controls object, e.g.:
30
+ - Pinterest: controls.pinterestBoardId is REQUIRED — it is a board's boardId from list_pinterest_boards, NOT the Pinterest account's socialMediaId.
31
+ - Google Business Profile: controls.gbpLocationId is required — the locationId from list_gbp_locations.
32
+ - YouTube: controls.youtubePlaylistId is the playlistId from list_youtube_playlists; youtubeIsShort defaults true; title falls back to the first 100 chars of content.
33
+ - TikTok: controls.tiktokPrivacy is deprecated (videos use the account default, photos default to public); tiktokTitle applies to photo carousels only (max 90); firstComment supported on Business accounts (max 150, comments must be enabled).
34
+ - Instagram: controls.instagramPublishType = TIMELINE | STORY | REEL.
35
+ - Facebook: controls.facebookContentType = POST | REEL | STORY. controls.facebookTargetCountries limits who can see a FEED post by country (ISO 3166-1 alpha-2, max 25; not Reels/Stories).
36
+ - Geotag a place (Facebook/Instagram): call search_places, then pass a returned id as controls.facebookPlaceId (Facebook feed posts only — not Reels/Stories/video) and/or controls.instagramLocationId (Instagram single media only — not carousels). One id works for both.
37
+ - LinkedIn: attach a document via controls.linkedinAttachmentKey.
38
+ - X: controls.xRetweetUrl reposts an existing tweet (content/media are ignored).
39
+ Note: list_pinterest_boards / list_youtube_playlists / list_gbp_locations take the account's socialMediaId; each returned item has BOTH an internal id and the platform id (boardId / playlistId / locationId) — pass the PLATFORM id to controls, not the internal id or the account id.
40
+
41
+ Failed posts carry lastError — usually a disconnected account (reconnect, then retry) or platform-rejected media.`;
42
+ const STDIO_INSTRUCTIONS = `PostFast schedules and publishes social posts across X, Instagram, Facebook, TikTok, LinkedIn, YouTube, Threads, Pinterest, Bluesky, Telegram, and Google Business Profile.
43
+
44
+ Flow: list_accounts to see what's connected → create_posts (one socialMediaId per post; batch up to 15) → attach media via upload_media (a local file, by absolute path) or get_upload_urls (signed PUT upload for raw bytes) → a post publishes when status=SCHEDULED and approvalStatus=APPROVED.
45
+
46
+ The POSTFAST_API_KEY is workspace-scoped — every tool acts in that workspace.
47
+
48
+ ${SHARED_TAIL}`;
49
+ const REMOTE_INSTRUCTIONS = `PostFast schedules and publishes social posts across X, Instagram, Facebook, TikTok, LinkedIn, YouTube, Threads, Pinterest, Bluesky, Telegram, and Google Business Profile.
50
+
51
+ Flow: list_accounts (+ list_workspaces) to see what's connected → create_posts (one socialMediaId per post; batch up to 15) → attach media via upload_from_url (a public https URL) or upload_media (a ChatGPT-attached/generated image, or base64) → a post publishes when status=SCHEDULED and approvalStatus=APPROVED.
52
+
53
+ Workspaces: omit workspaceId to use the connection's default; pass a workspaceId (from list_workspaces) to act in another workspace.
54
+
55
+ ${SHARED_TAIL}`;
56
+ export const SERVER_INSTRUCTIONS = {
57
+ stdio: STDIO_INSTRUCTIONS,
58
+ remote: REMOTE_INSTRUCTIONS,
59
+ };
60
+ export function instructionsFor(binding) {
61
+ return SERVER_INSTRUCTIONS[binding];
62
+ }
63
+ //# sourceMappingURL=instructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions.js","sourceRoot":"","sources":["../../src/core/instructions.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kHA8B8F,CAAC;AAEnH,MAAM,kBAAkB,GAAG;;;;;;EAMzB,WAAW,EAAE,CAAC;AAEhB,MAAM,mBAAmB,GAAG;;;;;;EAM1B,WAAW,EAAE,CAAC;AAEhB,MAAM,CAAC,MAAM,mBAAmB,GAA4B;IAC1D,KAAK,EAAE,kBAAkB;IACzB,MAAM,EAAE,mBAAmB;CAC5B,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { z } from 'zod';
2
+ export declare const PLATFORMS: readonly ["FACEBOOK", "INSTAGRAM", "X", "TIKTOK", "LINKEDIN", "YOUTUBE", "BLUESKY", "THREADS", "PINTEREST", "TELEGRAM", "GOOGLE_BUSINESS_PROFILE"];
3
+ export declare const POST_STATUSES: readonly ["DRAFT", "SCHEDULED", "PUBLISHED", "FAILED"];
4
+ export declare const INBOX_CONVERSATION_STATUSES: readonly ["OPEN", "SNOOZED", "CLOSED"];
5
+ export declare const INBOX_ITEM_STATE_ACTIONS: readonly ["HIDE", "UNHIDE", "DELETE"];
6
+ export declare const CREATE_STATUSES: readonly ["DRAFT", "SCHEDULED"];
7
+ export declare const CREATE_APPROVAL_STATUSES: readonly ["PENDING_APPROVAL", "APPROVED"];
8
+ export declare const SET_APPROVAL_STATUSES: readonly ["PENDING_APPROVAL", "IN_PROGRESS", "APPROVED", "REJECTED", "NEEDS_WORK"];
9
+ export declare const IMAGE_MIME_TYPES: readonly ["image/jpeg", "image/png", "image/gif", "image/webp"];
10
+ export declare const VIDEO_MIME_TYPES: readonly ["video/mp4", "video/webm", "video/quicktime"];
11
+ /** Some MCP clients stringify complex params — parse them back before validation. */
12
+ export declare function jsonParse<T extends z.ZodTypeAny>(schema: T): z.ZodPreprocess<T>;
13
+ /**
14
+ * Permissive output schema for list-style tools: { data: [...] }. Items are
15
+ * unknown (never type-rejected) and extra top-level keys (totalCount, pageInfo)
16
+ * pass through — so structuredContent is advertised without breaking on real
17
+ * backend responses.
18
+ */
19
+ export declare const dataListOutputSchema: {
20
+ data: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
21
+ };
22
+ /** Optional per-call workspace selector, injected only on the remote binding. */
23
+ export declare const workspaceIdField: z.ZodOptional<z.ZodUUID>;
@@ -0,0 +1,60 @@
1
+ import { z } from 'zod';
2
+ export const PLATFORMS = [
3
+ 'FACEBOOK',
4
+ 'INSTAGRAM',
5
+ 'X',
6
+ 'TIKTOK',
7
+ 'LINKEDIN',
8
+ 'YOUTUBE',
9
+ 'BLUESKY',
10
+ 'THREADS',
11
+ 'PINTEREST',
12
+ 'TELEGRAM',
13
+ 'GOOGLE_BUSINESS_PROFILE',
14
+ ];
15
+ export const POST_STATUSES = ['DRAFT', 'SCHEDULED', 'PUBLISHED', 'FAILED'];
16
+ export const INBOX_CONVERSATION_STATUSES = ['OPEN', 'SNOOZED', 'CLOSED'];
17
+ export const INBOX_ITEM_STATE_ACTIONS = ['HIDE', 'UNHIDE', 'DELETE'];
18
+ export const CREATE_STATUSES = ['DRAFT', 'SCHEDULED'];
19
+ export const CREATE_APPROVAL_STATUSES = ['PENDING_APPROVAL', 'APPROVED'];
20
+ export const SET_APPROVAL_STATUSES = [
21
+ 'PENDING_APPROVAL',
22
+ 'IN_PROGRESS',
23
+ 'APPROVED',
24
+ 'REJECTED',
25
+ 'NEEDS_WORK',
26
+ ];
27
+ export const IMAGE_MIME_TYPES = [
28
+ 'image/jpeg',
29
+ 'image/png',
30
+ 'image/gif',
31
+ 'image/webp',
32
+ ];
33
+ export const VIDEO_MIME_TYPES = ['video/mp4', 'video/webm', 'video/quicktime'];
34
+ /** Some MCP clients stringify complex params — parse them back before validation. */
35
+ export function jsonParse(schema) {
36
+ return z.preprocess((val) => {
37
+ if (typeof val === 'string') {
38
+ try {
39
+ return JSON.parse(val);
40
+ }
41
+ catch {
42
+ return val;
43
+ }
44
+ }
45
+ return val;
46
+ }, schema);
47
+ }
48
+ /**
49
+ * Permissive output schema for list-style tools: { data: [...] }. Items are
50
+ * unknown (never type-rejected) and extra top-level keys (totalCount, pageInfo)
51
+ * pass through — so structuredContent is advertised without breaking on real
52
+ * backend responses.
53
+ */
54
+ export const dataListOutputSchema = { data: z.array(z.unknown()).optional() };
55
+ /** Optional per-call workspace selector, injected only on the remote binding. */
56
+ export const workspaceIdField = z
57
+ .uuid()
58
+ .optional()
59
+ .describe("Target workspace id (from list_workspaces). Omit to use the connection's default workspace.");
60
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/core/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,UAAU;IACV,WAAW;IACX,GAAG;IACH,QAAQ;IACR,UAAU;IACV,SAAS;IACT,SAAS;IACT,SAAS;IACT,WAAW;IACX,UAAU;IACV,yBAAyB;CACjB,CAAC;AAEX,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,CAAU,CAAC;AACpF,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAU,CAAC;AAClF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAC9E,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,WAAW,CAAU,CAAC;AAC/D,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,kBAAkB,EAAE,UAAU,CAAU,CAAC;AAClF,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,kBAAkB;IAClB,aAAa;IACb,UAAU;IACV,UAAU;IACV,YAAY;CACJ,CAAC;AAEX,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,YAAY;IACZ,WAAW;IACX,WAAW;IACX,YAAY;CACJ,CAAC;AACX,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,WAAW,EAAE,YAAY,EAAE,iBAAiB,CAAU,CAAC;AAExF,qFAAqF;AACrF,MAAM,UAAU,SAAS,CAAyB,MAAS;IACzD,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,EAAE;QAC1B,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,GAAG,CAAC;YACb,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,MAAM,CAAC,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;AAE9E,iFAAiF;AACjF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC;KAC9B,IAAI,EAAE;KACN,QAAQ,EAAE;KACV,QAAQ,CACP,6FAA6F,CAC9F,CAAC"}
@@ -0,0 +1,55 @@
1
+ import type { ZodRawShape } from 'zod';
2
+ import type { BackendPort } from './backend-port.js';
3
+ /** The two deployments a catalog tool can ship in. */
4
+ export type Binding = 'stdio' | 'remote';
5
+ export interface ToolAnnotations {
6
+ readOnlyHint: boolean;
7
+ destructiveHint: boolean;
8
+ idempotentHint?: boolean;
9
+ openWorldHint: boolean;
10
+ }
11
+ /**
12
+ * One tool as authored in the catalog. `description`/`inputSchema` may be a
13
+ * function of the binding for the few spots where the surfaces genuinely
14
+ * differ (upload-tool references — stdio uploads local files, the remote
15
+ * uploads conversation media/URLs). Everything else is binding-invariant.
16
+ */
17
+ export interface ToolDef {
18
+ name: string;
19
+ /** Which binding(s) expose the tool. */
20
+ binding: Binding | 'both';
21
+ title: string;
22
+ description: string | ((binding: Binding) => string);
23
+ inputSchema: ZodRawShape | ((binding: Binding) => ZodRawShape);
24
+ outputSchema?: ZodRawShape;
25
+ annotations: ToolAnnotations;
26
+ /** Extra tool metadata, e.g. the ChatGPT file-param marker on upload_media. */
27
+ _meta?: Record<string, unknown>;
28
+ /** False only for tools that are not scoped to a workspace (list_workspaces). */
29
+ workspaceScoped?: boolean;
30
+ /**
31
+ * The BackendPort method this tool's run() dispatches to. When set and the
32
+ * port instance lacks the method (an older adapter running a newer catalog),
33
+ * the tool is skipped at registration with a stderr log instead of shipping
34
+ * a broken tool — lets bindings adopt new tool waves on their own schedule.
35
+ */
36
+ portMethod?: keyof BackendPort;
37
+ /**
38
+ * Dispatch to the backend. `args` are the validated tool arguments minus
39
+ * `workspaceId`, which is split off by the registrar and passed separately
40
+ * (always undefined on stdio — the pf-api-key is already workspace-scoped).
41
+ */
42
+ run: (port: BackendPort, args: Record<string, unknown>, workspaceId?: string) => Promise<unknown>;
43
+ }
44
+ /** A ToolDef with binding-dependent fields resolved for one concrete binding. */
45
+ export interface ResolvedTool {
46
+ name: string;
47
+ title: string;
48
+ description: string;
49
+ inputSchema: ZodRawShape;
50
+ outputSchema?: ZodRawShape;
51
+ annotations: ToolAnnotations;
52
+ _meta?: Record<string, unknown>;
53
+ portMethod?: keyof BackendPort;
54
+ run: ToolDef['run'];
55
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tool-def.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-def.js","sourceRoot":"","sources":["../../src/core/tool-def.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ import type { ToolDef } from '../tool-def.js';
2
+ export declare const accountTools: ToolDef[];