postfast-mcp 0.1.23 → 0.2.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 (62) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +11 -5
  4. package/dist/core/backend-port.d.ts +97 -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 +32 -0
  8. package/dist/core/build-tools.js +73 -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 +21 -0
  17. package/dist/core/shared.js +58 -0
  18. package/dist/core/shared.js.map +1 -0
  19. package/dist/core/tool-def.d.ts +47 -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/index.d.ts +7 -0
  26. package/dist/core/tools/index.js +16 -0
  27. package/dist/core/tools/index.js.map +1 -0
  28. package/dist/core/tools/posts.d.ts +2 -0
  29. package/dist/core/tools/posts.js +264 -0
  30. package/dist/core/tools/posts.js.map +1 -0
  31. package/dist/core/tools/uploads.d.ts +8 -0
  32. package/dist/core/tools/uploads.js +102 -0
  33. package/dist/core/tools/uploads.js.map +1 -0
  34. package/dist/core/tools/workspaces.d.ts +2 -0
  35. package/dist/core/tools/workspaces.js +15 -0
  36. package/dist/core/tools/workspaces.js.map +1 -0
  37. package/dist/{types.d.ts → core/types.d.ts} +7 -1
  38. package/dist/core/types.js.map +1 -0
  39. package/dist/stdio/index.js +15 -0
  40. package/dist/stdio/index.js.map +1 -0
  41. package/dist/stdio/rest-adapter.d.ts +27 -0
  42. package/dist/stdio/rest-adapter.js +170 -0
  43. package/dist/stdio/rest-adapter.js.map +1 -0
  44. package/package.json +22 -8
  45. package/skills/social-media-post/SKILL.md +1 -0
  46. package/dist/client.d.ts +0 -9
  47. package/dist/client.js +0 -57
  48. package/dist/client.js.map +0 -1
  49. package/dist/index.js +0 -18
  50. package/dist/index.js.map +0 -1
  51. package/dist/tools/accounts.d.ts +0 -3
  52. package/dist/tools/accounts.js +0 -118
  53. package/dist/tools/accounts.js.map +0 -1
  54. package/dist/tools/files.d.ts +0 -3
  55. package/dist/tools/files.js +0 -87
  56. package/dist/tools/files.js.map +0 -1
  57. package/dist/tools/posts.d.ts +0 -3
  58. package/dist/tools/posts.js +0 -228
  59. package/dist/tools/posts.js.map +0 -1
  60. package/dist/types.js.map +0 -1
  61. /package/dist/{types.js → core/types.js} +0 -0
  62. /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.22",
14
+ "version": "0.2.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.22",
4
+ "version": "0.2.0",
5
5
  "author": {
6
6
  "name": "PostFast",
7
7
  "email": "me@peturgeorgievv.com"
package/README.md CHANGED
@@ -119,7 +119,7 @@ Ask your AI assistant things like:
119
119
 
120
120
  | Tool | Description |
121
121
  |------|-------------|
122
- | `list_accounts` | List connected social media accounts (each with `connectionStatus` — `CONNECTED`/`DISABLED` — and `disabledReason`) |
122
+ | `list_accounts` | List connected social media accounts (each with `connectionStatus` — `CONNECTED`/`DISABLED` — `disabledReason`, `followerCount`/`followerCountUpdatedAt`, and `inboxCapable`) |
123
123
  | `list_posts` | List posts with filters (specific IDs, platform, status, date range) |
124
124
  | `create_posts` | Create and schedule posts (batch, up to 15) |
125
125
  | `delete_post` | Delete a post by ID |
@@ -131,7 +131,7 @@ Ask your AI assistant things like:
131
131
  | `search_places` | Find a place to tag posts (the `id` works for both `facebookPlaceId` and `instagramLocationId`) |
132
132
  | `generate_connect_link` | Generate a link for clients to connect accounts |
133
133
  | `get_post_analytics` | Fetch published posts with performance metrics — Instagram, Facebook, TikTok, Threads, YouTube, LinkedIn (company pages), Pinterest (Business accounts) |
134
- | `get_follower_history` | Daily follower-count history for an account (current count + `delta` over a date range) — Facebook Pages, Instagram, YouTube, Pinterest, Threads, Bluesky, Telegram, LinkedIn (company pages) |
134
+ | `get_follower_history` | Daily follower-count history for an account (current count + `delta` over a date range) — Facebook Pages, Instagram, YouTube, Pinterest, Threads, Bluesky, Telegram, LinkedIn (company pages), TikTok |
135
135
 
136
136
  ## Supported Platforms
137
137
 
@@ -145,8 +145,8 @@ When creating posts, you can pass platform-specific settings via the `controls`
145
145
  |----------|----------|
146
146
  | **X (Twitter)** | `xRetweetUrl` |
147
147
  | **Instagram** | `instagramPublishType` (TIMELINE/STORY/REEL), `instagramPostToGrid`, `instagramCollaborators`, `instagramLocationId`/`instagramLocationName` (geotag from `search_places`) |
148
- | **Facebook** | `facebookContentType` (POST/REEL/STORY), `facebookAllowComments`, `facebookPrivacy`, `facebookReelsCollaborators`, `facebookTargetCountries` (ISO alpha-2, max 25), `facebookPlaceId`/`facebookPlaceName` (geotag from `search_places`) |
149
- | **TikTok** | `tiktokPrivacy` (PUBLIC/MUTUAL_FRIENDS/FOLLOWER_OF_CREATOR/ONLY_ME), `tiktokIsDraft`, `tiktokAllowComments`, `tiktokAllowDuet`, `tiktokAllowStitch`, `tiktokBrandOrganic`, `tiktokBrandContent`, `tiktokAutoAddMusic`, `tiktokIsAigc` |
148
+ | **Facebook** | `facebookContentType` (POST/REEL/STORY), `facebookAllowComments`, `facebookPrivacy`, `facebookCarouselMainLink`, `facebookCarouselShowEndCard`, `facebookReelsCollaborators`, `facebookTargetCountries` (ISO alpha-2, max 25), `facebookPlaceId`/`facebookPlaceName` (geotag from `search_places`) |
149
+ | **TikTok** | `tiktokTitle` (photo posts, max 90 chars), `tiktokIsDraft`, `tiktokAllowComments`, `tiktokAllowDuet`, `tiktokAllowStitch`, `tiktokBrandOrganic`, `tiktokBrandContent`, `tiktokAutoAddMusic`, `tiktokIsAigc`, `tiktokPrivacy` (deprecated — account default applies) |
150
150
  | **YouTube** | `youtubeTitle`, `youtubePrivacy` (PUBLIC/PRIVATE/UNLISTED), `youtubeTags`, `youtubeCategoryId`, `youtubeIsShort`, `youtubeMadeForKids`, `youtubePlaylistId`, `youtubeThumbnailKey` |
151
151
  | **Pinterest** | `pinterestBoardId` (required), `pinterestLink` |
152
152
  | **LinkedIn** | `linkedinAttachmentKey`, `linkedinAttachmentTitle` |
@@ -189,9 +189,15 @@ 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 go through changesets: PRs add a `.changeset/*.md`, and merging the
199
+ generated Version Packages PR publishes npm + MCP Registry + the MCPB release.
200
+
195
201
  ## Badges
196
202
 
197
203
  [![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,97 @@
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 UploadFromUrlArgs {
42
+ sourceUrl: string;
43
+ contentType?: string;
44
+ }
45
+ /** Remote conversation-media upload: a client-provided file ref or base64 bytes. */
46
+ export interface UploadMediaArgs {
47
+ file?: {
48
+ download_url: string;
49
+ file_id: string;
50
+ mime_type?: string;
51
+ file_name?: string;
52
+ };
53
+ data?: string;
54
+ contentType?: string;
55
+ }
56
+ export interface LocalUploadResult {
57
+ key: string;
58
+ type: 'IMAGE' | 'VIDEO';
59
+ contentType: string;
60
+ }
61
+ /**
62
+ * Everything the catalog tools need from a backend. The stdio bin implements
63
+ * it over the public REST API (pf-api-key); the deployed host implements it
64
+ * over its command gateway + upload service. `workspaceId` is only ever set on
65
+ * the remote binding — adapters without workspace switching ignore it.
66
+ *
67
+ * Adapters may throw from methods whose tools they never register
68
+ * (e.g. the REST adapter for the remote-only conversation-media uploads).
69
+ */
70
+ export interface BackendPort {
71
+ listWorkspaces(): Promise<unknown>;
72
+ listAccounts(workspaceId?: string): Promise<SocialAccount[] | unknown>;
73
+ getFollowerHistory(args: FollowerHistoryArgs, workspaceId?: string): Promise<FollowerHistory | unknown>;
74
+ listPinterestBoards(socialMediaId: string, workspaceId?: string): Promise<PinterestBoard[] | unknown>;
75
+ listYoutubePlaylists(socialMediaId: string, workspaceId?: string): Promise<YouTubePlaylist[] | unknown>;
76
+ listGbpLocations(socialMediaId: string, workspaceId?: string): Promise<GbpLocation[] | unknown>;
77
+ searchPlaces(query: string, workspaceId?: string): Promise<Place[] | unknown>;
78
+ generateConnectLink(args: ConnectLinkArgs, workspaceId?: string): Promise<{
79
+ connectUrl: string;
80
+ } | unknown>;
81
+ listPosts(args: ListPostsArgs, workspaceId?: string): Promise<PaginatedPosts | unknown>;
82
+ createPosts(args: CreatePostsArgs, workspaceId?: string): Promise<{
83
+ postIds: string[];
84
+ } | unknown>;
85
+ approvePosts(args: ApprovePostsArgs, workspaceId?: string): Promise<unknown>;
86
+ deletePost(id: string, workspaceId?: string): Promise<{
87
+ deleted: boolean;
88
+ } | unknown>;
89
+ getPostAnalytics(args: AnalyticsArgs, workspaceId?: string): Promise<AnalyticsResponse | unknown>;
90
+ getUploadUrls(args: UploadUrlsArgs, workspaceId?: string): Promise<SignedUploadUrl[] | unknown>;
91
+ /** stdio only: read a local file and push it through the signed-URL flow. */
92
+ uploadLocalFile(filePath: string): Promise<LocalUploadResult>;
93
+ /** Remote only: server-side fetch of a public https URL (SSRF-guarded in the host). */
94
+ uploadFromUrl(args: UploadFromUrlArgs, workspaceId?: string): Promise<unknown>;
95
+ /** Remote only: upload a conversation-attached file or base64 bytes. */
96
+ uploadMedia(args: UploadMediaArgs, workspaceId?: string): Promise<unknown>;
97
+ }
@@ -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,32 @@
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
+ /** Resolve the catalog for one binding: filter, flatten binding-variant fields. */
15
+ export declare function buildTools(options: BuildToolsOptions): ResolvedTool[];
16
+ /**
17
+ * Wrap backend data as MCP content: a JSON text block (proven across all
18
+ * clients — and byte-stable for existing stdio consumers) plus
19
+ * structuredContent for clients that consume outputSchema. Bare-array
20
+ * responses are wrapped as { data } there, since structuredContent must be an
21
+ * object; the text block stays the raw response.
22
+ */
23
+ export declare function toolResult(data: unknown): CallToolResult;
24
+ /** A business/tool error the model should see and react to (not a protocol error). */
25
+ export declare function toolError(message: string): CallToolResult;
26
+ /** Run a handler, converting thrown errors into `isError` tool results. */
27
+ export declare function runTool(fn: () => Promise<unknown>): Promise<CallToolResult>;
28
+ export interface RegisterToolsOptions extends BuildToolsOptions {
29
+ port: BackendPort;
30
+ }
31
+ /** Register the resolved catalog for a binding on an MCP server. */
32
+ export declare function registerCatalogTools(server: McpServer, options: RegisterToolsOptions): void;
@@ -0,0 +1,73 @@
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 } = options;
6
+ return ALL_TOOLS.filter((def) => def.binding === 'both' || def.binding === binding).map((def) => {
7
+ const inputSchema = typeof def.inputSchema === 'function' ? def.inputSchema(binding) : def.inputSchema;
8
+ return {
9
+ name: def.name,
10
+ title: def.title,
11
+ description: typeof def.description === 'function' ? def.description(binding) : def.description,
12
+ inputSchema: withWorkspaceField && def.workspaceScoped !== false
13
+ ? { ...inputSchema, workspaceId: workspaceIdField }
14
+ : inputSchema,
15
+ outputSchema: def.outputSchema,
16
+ annotations: def.annotations,
17
+ _meta: def._meta,
18
+ run: def.run,
19
+ };
20
+ });
21
+ }
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 function toolResult(data) {
30
+ const result = {
31
+ content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
32
+ };
33
+ if (data && typeof data === 'object') {
34
+ result.structuredContent = Array.isArray(data)
35
+ ? { data }
36
+ : data;
37
+ }
38
+ return result;
39
+ }
40
+ /** A business/tool error the model should see and react to (not a protocol error). */
41
+ export function toolError(message) {
42
+ return {
43
+ content: [{ type: 'text', text: message }],
44
+ isError: true,
45
+ };
46
+ }
47
+ /** Run a handler, converting thrown errors into `isError` tool results. */
48
+ export async function runTool(fn) {
49
+ try {
50
+ return toolResult(await fn());
51
+ }
52
+ catch (err) {
53
+ return toolError(err.message || 'Tool execution failed.');
54
+ }
55
+ }
56
+ /** Register the resolved catalog for a binding on an MCP server. */
57
+ export function registerCatalogTools(server, options) {
58
+ const { port } = options;
59
+ for (const tool of buildTools(options)) {
60
+ server.registerTool(tool.name, {
61
+ title: tool.title,
62
+ description: tool.description,
63
+ inputSchema: tool.inputSchema,
64
+ ...(tool.outputSchema ? { outputSchema: tool.outputSchema } : {}),
65
+ annotations: tool.annotations,
66
+ ...(tool._meta ? { _meta: tool._meta } : {}),
67
+ }, (args) => runTool(() => {
68
+ const { workspaceId, ...rest } = args ?? {};
69
+ return tool.run(port, rest, workspaceId);
70
+ }));
71
+ }
72
+ }
73
+ //# 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,21 @@
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 CREATE_STATUSES: readonly ["DRAFT", "SCHEDULED"];
5
+ export declare const CREATE_APPROVAL_STATUSES: readonly ["PENDING_APPROVAL", "APPROVED"];
6
+ export declare const SET_APPROVAL_STATUSES: readonly ["PENDING_APPROVAL", "IN_PROGRESS", "APPROVED", "REJECTED", "NEEDS_WORK"];
7
+ export declare const IMAGE_MIME_TYPES: readonly ["image/jpeg", "image/png", "image/gif", "image/webp"];
8
+ export declare const VIDEO_MIME_TYPES: readonly ["video/mp4", "video/webm", "video/quicktime"];
9
+ /** Some MCP clients stringify complex params — parse them back before validation. */
10
+ export declare function jsonParse<T extends z.ZodTypeAny>(schema: T): z.ZodPreprocess<T>;
11
+ /**
12
+ * Permissive output schema for list-style tools: { data: [...] }. Items are
13
+ * unknown (never type-rejected) and extra top-level keys (totalCount, pageInfo)
14
+ * pass through — so structuredContent is advertised without breaking on real
15
+ * backend responses.
16
+ */
17
+ export declare const dataListOutputSchema: {
18
+ data: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
19
+ };
20
+ /** Optional per-call workspace selector, injected only on the remote binding. */
21
+ export declare const workspaceIdField: z.ZodOptional<z.ZodUUID>;
@@ -0,0 +1,58 @@
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 CREATE_STATUSES = ['DRAFT', 'SCHEDULED'];
17
+ export const CREATE_APPROVAL_STATUSES = ['PENDING_APPROVAL', 'APPROVED'];
18
+ export const SET_APPROVAL_STATUSES = [
19
+ 'PENDING_APPROVAL',
20
+ 'IN_PROGRESS',
21
+ 'APPROVED',
22
+ 'REJECTED',
23
+ 'NEEDS_WORK',
24
+ ];
25
+ export const IMAGE_MIME_TYPES = [
26
+ 'image/jpeg',
27
+ 'image/png',
28
+ 'image/gif',
29
+ 'image/webp',
30
+ ];
31
+ export const VIDEO_MIME_TYPES = ['video/mp4', 'video/webm', 'video/quicktime'];
32
+ /** Some MCP clients stringify complex params — parse them back before validation. */
33
+ export function jsonParse(schema) {
34
+ return z.preprocess((val) => {
35
+ if (typeof val === 'string') {
36
+ try {
37
+ return JSON.parse(val);
38
+ }
39
+ catch {
40
+ return val;
41
+ }
42
+ }
43
+ return val;
44
+ }, schema);
45
+ }
46
+ /**
47
+ * Permissive output schema for list-style tools: { data: [...] }. Items are
48
+ * unknown (never type-rejected) and extra top-level keys (totalCount, pageInfo)
49
+ * pass through — so structuredContent is advertised without breaking on real
50
+ * backend responses.
51
+ */
52
+ export const dataListOutputSchema = { data: z.array(z.unknown()).optional() };
53
+ /** Optional per-call workspace selector, injected only on the remote binding. */
54
+ export const workspaceIdField = z
55
+ .uuid()
56
+ .optional()
57
+ .describe("Target workspace id (from list_workspaces). Omit to use the connection's default workspace.");
58
+ //# 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,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,47 @@
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
+ * Dispatch to the backend. `args` are the validated tool arguments minus
32
+ * `workspaceId`, which is split off by the registrar and passed separately
33
+ * (always undefined on stdio — the pf-api-key is already workspace-scoped).
34
+ */
35
+ run: (port: BackendPort, args: Record<string, unknown>, workspaceId?: string) => Promise<unknown>;
36
+ }
37
+ /** A ToolDef with binding-dependent fields resolved for one concrete binding. */
38
+ export interface ResolvedTool {
39
+ name: string;
40
+ title: string;
41
+ description: string;
42
+ inputSchema: ZodRawShape;
43
+ outputSchema?: ZodRawShape;
44
+ annotations: ToolAnnotations;
45
+ _meta?: Record<string, unknown>;
46
+ run: ToolDef['run'];
47
+ }
@@ -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[];