postfast-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.js +57 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/accounts.d.ts +3 -0
- package/dist/tools/accounts.js +59 -0
- package/dist/tools/accounts.js.map +1 -0
- package/dist/tools/files.d.ts +3 -0
- package/dist/tools/files.js +81 -0
- package/dist/tools/files.js.map +1 -0
- package/dist/tools/posts.d.ts +3 -0
- package/dist/tools/posts.js +154 -0
- package/dist/tools/posts.js.map +1 -0
- package/dist/types.d.ts +103 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# PostFast MCP Server
|
|
2
|
+
|
|
3
|
+
MCP server for the [PostFast](https://postfa.st) API — schedule and manage social media posts via AI tools like Claude.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
### 1. Get your API key
|
|
8
|
+
|
|
9
|
+
Go to **PostFast → Workspace Settings → API Key** and generate a key.
|
|
10
|
+
|
|
11
|
+
### 2. Configure in Claude Code
|
|
12
|
+
|
|
13
|
+
Add to your `.mcp.json` (project) or `~/.claude.json` (global):
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"postfast": {
|
|
19
|
+
"type": "stdio",
|
|
20
|
+
"command": "npx",
|
|
21
|
+
"args": ["postfast-mcp"],
|
|
22
|
+
"env": {
|
|
23
|
+
"POSTFAST_API_KEY": "your-api-key-here"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 3. Use it
|
|
31
|
+
|
|
32
|
+
Ask Claude things like:
|
|
33
|
+
|
|
34
|
+
- "List my connected social accounts"
|
|
35
|
+
- "Schedule a post to Instagram for tomorrow at 9am"
|
|
36
|
+
- "Show me all scheduled posts for this week"
|
|
37
|
+
- "Upload this image and create a LinkedIn post with it"
|
|
38
|
+
|
|
39
|
+
## Available Tools
|
|
40
|
+
|
|
41
|
+
| Tool | Description |
|
|
42
|
+
|------|-------------|
|
|
43
|
+
| `list_posts` | List posts with filters (platform, status, date range) |
|
|
44
|
+
| `create_posts` | Create and schedule posts (batch, up to 15) |
|
|
45
|
+
| `delete_post` | Delete a post by ID |
|
|
46
|
+
| `list_accounts` | List connected social media accounts |
|
|
47
|
+
| `list_pinterest_boards` | Get Pinterest boards for an account |
|
|
48
|
+
| `list_youtube_playlists` | Get YouTube playlists for an account |
|
|
49
|
+
| `generate_connect_link` | Generate a link for clients to connect accounts |
|
|
50
|
+
| `get_upload_urls` | Get signed URLs to upload media files |
|
|
51
|
+
| `upload_media` | Upload a local file and get a media key (handles the full flow) |
|
|
52
|
+
|
|
53
|
+
## Supported Platforms
|
|
54
|
+
|
|
55
|
+
Facebook, Instagram, X (Twitter), TikTok, LinkedIn, YouTube, BlueSky, Threads, Pinterest, Telegram
|
|
56
|
+
|
|
57
|
+
## Media Upload
|
|
58
|
+
|
|
59
|
+
The `upload_media` tool handles the full flow in a single call:
|
|
60
|
+
|
|
61
|
+
1. Detects content type from file extension
|
|
62
|
+
2. Gets a signed upload URL from PostFast
|
|
63
|
+
3. Uploads the file
|
|
64
|
+
4. Returns a `key` and `type` ready to use in `create_posts`
|
|
65
|
+
|
|
66
|
+
Supported formats: JPEG, PNG, GIF, WebP, MP4, WebM, MOV
|
|
67
|
+
|
|
68
|
+
You can also use `get_upload_urls` directly if you need more control over the upload process.
|
|
69
|
+
|
|
70
|
+
## Environment Variables
|
|
71
|
+
|
|
72
|
+
| Variable | Required | Description |
|
|
73
|
+
|----------|----------|-------------|
|
|
74
|
+
| `POSTFAST_API_KEY` | Yes | Your workspace API key |
|
|
75
|
+
| `POSTFAST_API_URL` | No | API base URL (default: `https://api.postfa.st`) |
|
|
76
|
+
|
|
77
|
+
## API Docs
|
|
78
|
+
|
|
79
|
+
Full REST API documentation: [postfa.st/docs](https://postfa.st/docs)
|
|
80
|
+
|
|
81
|
+
## Development
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npm install
|
|
85
|
+
npm run build
|
|
86
|
+
node dist/index.js
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class PostFastClient {
|
|
2
|
+
private baseUrl;
|
|
3
|
+
private apiKey;
|
|
4
|
+
constructor();
|
|
5
|
+
request<T>(method: string, path: string, body?: unknown, query?: Record<string, string | undefined>): Promise<T>;
|
|
6
|
+
get<T>(path: string, query?: Record<string, string | undefined>): Promise<T>;
|
|
7
|
+
post<T>(path: string, body?: unknown): Promise<T>;
|
|
8
|
+
delete<T>(path: string): Promise<T>;
|
|
9
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const DEFAULT_BASE_URL = 'https://api.postfa.st';
|
|
2
|
+
export class PostFastClient {
|
|
3
|
+
baseUrl;
|
|
4
|
+
apiKey;
|
|
5
|
+
constructor() {
|
|
6
|
+
this.apiKey = process.env.POSTFAST_API_KEY ?? '';
|
|
7
|
+
this.baseUrl = (process.env.POSTFAST_API_URL ?? DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
8
|
+
if (!this.apiKey) {
|
|
9
|
+
throw new Error('POSTFAST_API_KEY environment variable is required. Generate one in PostFast → Workspace Settings → API Key.');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
async request(method, path, body, query) {
|
|
13
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
14
|
+
if (query) {
|
|
15
|
+
for (const [key, value] of Object.entries(query)) {
|
|
16
|
+
if (value !== undefined && value !== '') {
|
|
17
|
+
url.searchParams.set(key, value);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const headers = {
|
|
22
|
+
'pf-api-key': this.apiKey,
|
|
23
|
+
'Content-Type': 'application/json',
|
|
24
|
+
};
|
|
25
|
+
const response = await fetch(url.toString(), {
|
|
26
|
+
method,
|
|
27
|
+
headers,
|
|
28
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
const text = await response.text();
|
|
32
|
+
let message;
|
|
33
|
+
try {
|
|
34
|
+
const json = JSON.parse(text);
|
|
35
|
+
message = json.message ?? json.error ?? text;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
message = text;
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`PostFast API error (${response.status}): ${message}`);
|
|
41
|
+
}
|
|
42
|
+
const text = await response.text();
|
|
43
|
+
if (!text)
|
|
44
|
+
return undefined;
|
|
45
|
+
return JSON.parse(text);
|
|
46
|
+
}
|
|
47
|
+
get(path, query) {
|
|
48
|
+
return this.request('GET', path, undefined, query);
|
|
49
|
+
}
|
|
50
|
+
post(path, body) {
|
|
51
|
+
return this.request('POST', path, body);
|
|
52
|
+
}
|
|
53
|
+
delete(path) {
|
|
54
|
+
return this.request('DELETE', path);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;AAEjD,MAAM,OAAO,cAAc;IACjB,OAAO,CAAS;IAChB,MAAM,CAAS;IAEvB;QACE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC;QACjD,IAAI,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,gBAAgB,CAAC,CAAC,OAAO,CACvE,KAAK,EACL,EAAE,CACH,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CACb,6GAA6G,CAC9G,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CACX,MAAc,EACd,IAAY,EACZ,IAAc,EACd,KAA0C;QAE1C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC,CAAC;QAE9C,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;oBACxC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAA2B;YACtC,YAAY,EAAE,IAAI,CAAC,MAAM;YACzB,cAAc,EAAE,kBAAkB;SACnC,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YAC3C,MAAM;YACN,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SAC9C,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,OAAe,CAAC;YAEpB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC9B,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;YAED,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,MAAM,MAAM,OAAO,EAAE,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,CAAC,IAAI;YAAE,OAAO,SAAc,CAAC;QAEjC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;IAC/B,CAAC;IAED,GAAG,CAAI,IAAY,EAAE,KAA0C;QAC7D,OAAO,IAAI,CAAC,OAAO,CAAI,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IACxD,CAAC;IAED,IAAI,CAAI,IAAY,EAAE,IAAc;QAClC,OAAO,IAAI,CAAC,OAAO,CAAI,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,CAAI,IAAY;QACpB,OAAO,IAAI,CAAC,OAAO,CAAI,QAAQ,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { PostFastClient } from './client.js';
|
|
5
|
+
import { registerPostTools } from './tools/posts.js';
|
|
6
|
+
import { registerAccountTools } from './tools/accounts.js';
|
|
7
|
+
import { registerFileTools } from './tools/files.js';
|
|
8
|
+
const server = new McpServer({
|
|
9
|
+
name: 'postfast',
|
|
10
|
+
version: '0.1.0',
|
|
11
|
+
});
|
|
12
|
+
const client = new PostFastClient();
|
|
13
|
+
registerPostTools(server, client);
|
|
14
|
+
registerAccountTools(server, client);
|
|
15
|
+
registerFileTools(server, client);
|
|
16
|
+
const transport = new StdioServerTransport();
|
|
17
|
+
await server.connect(transport);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;AAEpC,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAClC,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACrC,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAElC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerAccountTools(server, client) {
|
|
3
|
+
server.tool('list_accounts', 'List all social media accounts connected to the workspace', {}, async () => {
|
|
4
|
+
const data = await client.get('/social-media/my-social-accounts');
|
|
5
|
+
return {
|
|
6
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
7
|
+
};
|
|
8
|
+
});
|
|
9
|
+
server.tool('list_pinterest_boards', 'List Pinterest boards for a connected Pinterest account', {
|
|
10
|
+
socialMediaId: z
|
|
11
|
+
.string()
|
|
12
|
+
.uuid()
|
|
13
|
+
.describe('Pinterest account ID (from list_accounts)'),
|
|
14
|
+
}, async (input) => {
|
|
15
|
+
const data = await client.get(`/social-media/${input.socialMediaId}/pinterest-boards`);
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
server.tool('list_youtube_playlists', 'List YouTube playlists for a connected YouTube account', {
|
|
21
|
+
socialMediaId: z
|
|
22
|
+
.string()
|
|
23
|
+
.uuid()
|
|
24
|
+
.describe('YouTube account ID (from list_accounts)'),
|
|
25
|
+
}, async (input) => {
|
|
26
|
+
const data = await client.get(`/social-media/${input.socialMediaId}/youtube-playlists`);
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
server.tool('generate_connect_link', 'Generate a shareable link for external clients to connect their social accounts to the workspace', {
|
|
32
|
+
expiryDays: z
|
|
33
|
+
.number()
|
|
34
|
+
.int()
|
|
35
|
+
.min(1)
|
|
36
|
+
.max(30)
|
|
37
|
+
.default(7)
|
|
38
|
+
.describe('Link expiry in days (1-30, default 7)'),
|
|
39
|
+
sendEmail: z
|
|
40
|
+
.boolean()
|
|
41
|
+
.default(false)
|
|
42
|
+
.describe('Send the link via email'),
|
|
43
|
+
email: z
|
|
44
|
+
.string()
|
|
45
|
+
.email()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe('Recipient email (required if sendEmail is true)'),
|
|
48
|
+
}, async (input) => {
|
|
49
|
+
const data = await client.post('/social-media/connect-link', {
|
|
50
|
+
expiryDays: input.expiryDays,
|
|
51
|
+
sendEmail: input.sendEmail,
|
|
52
|
+
email: input.email,
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=accounts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/tools/accounts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,MAAM,UAAU,oBAAoB,CAClC,MAAiB,EACjB,MAAsB;IAEtB,MAAM,CAAC,IAAI,CACT,eAAe,EACf,2DAA2D,EAC3D,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAC3B,kCAAkC,CACnC,CAAC;QAEF,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC1E,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,uBAAuB,EACvB,yDAAyD,EACzD;QACE,aAAa,EAAE,CAAC;aACb,MAAM,EAAE;aACR,IAAI,EAAE;aACN,QAAQ,CAAC,2CAA2C,CAAC;KACzD,EACD,KAAK,EAAE,KAAK,EAAE,EAAE;QACd,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAC3B,iBAAiB,KAAK,CAAC,aAAa,mBAAmB,CACxD,CAAC;QAEF,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC1E,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,wBAAwB,EACxB,wDAAwD,EACxD;QACE,aAAa,EAAE,CAAC;aACb,MAAM,EAAE;aACR,IAAI,EAAE;aACN,QAAQ,CAAC,yCAAyC,CAAC;KACvD,EACD,KAAK,EAAE,KAAK,EAAE,EAAE;QACd,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAC3B,iBAAiB,KAAK,CAAC,aAAa,oBAAoB,CACzD,CAAC;QAEF,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC1E,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,uBAAuB,EACvB,kGAAkG,EAClG;QACE,UAAU,EAAE,CAAC;aACV,MAAM,EAAE;aACR,GAAG,EAAE;aACL,GAAG,CAAC,CAAC,CAAC;aACN,GAAG,CAAC,EAAE,CAAC;aACP,OAAO,CAAC,CAAC,CAAC;aACV,QAAQ,CAAC,uCAAuC,CAAC;QACpD,SAAS,EAAE,CAAC;aACT,OAAO,EAAE;aACT,OAAO,CAAC,KAAK,CAAC;aACd,QAAQ,CAAC,yBAAyB,CAAC;QACtC,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,KAAK,EAAE;aACP,QAAQ,EAAE;aACV,QAAQ,CAAC,iDAAiD,CAAC;KAC/D,EACD,KAAK,EAAE,KAAK,EAAE,EAAE;QACd,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAC5B,4BAA4B,EAC5B;YACE,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB,CACF,CAAC;QAEF,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC1E,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { extname } from 'node:path';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
const MIME_MAP = {
|
|
5
|
+
'.jpg': 'image/jpeg',
|
|
6
|
+
'.jpeg': 'image/jpeg',
|
|
7
|
+
'.png': 'image/png',
|
|
8
|
+
'.gif': 'image/gif',
|
|
9
|
+
'.webp': 'image/webp',
|
|
10
|
+
'.mp4': 'video/mp4',
|
|
11
|
+
'.webm': 'video/webm',
|
|
12
|
+
'.mov': 'video/quicktime',
|
|
13
|
+
};
|
|
14
|
+
const IMAGE_TYPES = [
|
|
15
|
+
'image/jpeg',
|
|
16
|
+
'image/png',
|
|
17
|
+
'image/gif',
|
|
18
|
+
'image/webp',
|
|
19
|
+
];
|
|
20
|
+
const VIDEO_TYPES = ['video/mp4', 'video/webm', 'video/quicktime'];
|
|
21
|
+
function detectContentType(filePath) {
|
|
22
|
+
const ext = extname(filePath).toLowerCase();
|
|
23
|
+
const mime = MIME_MAP[ext];
|
|
24
|
+
if (!mime) {
|
|
25
|
+
throw new Error(`Unsupported file extension "${ext}". Supported: ${Object.keys(MIME_MAP).join(', ')}`);
|
|
26
|
+
}
|
|
27
|
+
return mime;
|
|
28
|
+
}
|
|
29
|
+
export function registerFileTools(server, client) {
|
|
30
|
+
server.tool('get_upload_urls', 'Get signed upload URLs for media files. Upload your file to the returned URL via PUT, then use the key in create_posts mediaItems.', {
|
|
31
|
+
contentType: z
|
|
32
|
+
.string()
|
|
33
|
+
.describe(`MIME type of the file. Supported: ${[...IMAGE_TYPES, ...VIDEO_TYPES].join(', ')}`),
|
|
34
|
+
count: z
|
|
35
|
+
.number()
|
|
36
|
+
.int()
|
|
37
|
+
.min(1)
|
|
38
|
+
.max(8)
|
|
39
|
+
.default(1)
|
|
40
|
+
.describe('Number of upload URLs (1-8 for images, 1 for videos)'),
|
|
41
|
+
}, async (input) => {
|
|
42
|
+
const data = await client.post('/file/get-signed-upload-urls', {
|
|
43
|
+
contentType: input.contentType,
|
|
44
|
+
count: input.count,
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
content: [
|
|
48
|
+
{ type: 'text', text: JSON.stringify(data, null, 2) },
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
server.tool('upload_media', 'Upload a local file to PostFast and get back a media key for use in create_posts. Handles the full flow: detects content type, gets a signed URL, uploads the file, and returns the key and type.', {
|
|
53
|
+
filePath: z
|
|
54
|
+
.string()
|
|
55
|
+
.describe('Absolute path to the local file (e.g. /Users/me/photo.jpg)'),
|
|
56
|
+
}, async (input) => {
|
|
57
|
+
const contentType = detectContentType(input.filePath);
|
|
58
|
+
const isVideo = contentType.startsWith('video/');
|
|
59
|
+
const [uploadUrl] = await client.post('/file/get-signed-upload-urls', { contentType, count: 1 });
|
|
60
|
+
const fileBuffer = await readFile(input.filePath);
|
|
61
|
+
const uploadResponse = await fetch(uploadUrl.signedUrl, {
|
|
62
|
+
method: 'PUT',
|
|
63
|
+
headers: { 'Content-Type': contentType },
|
|
64
|
+
body: fileBuffer,
|
|
65
|
+
});
|
|
66
|
+
if (!uploadResponse.ok) {
|
|
67
|
+
throw new Error(`Upload failed (${uploadResponse.status}): ${await uploadResponse.text()}`);
|
|
68
|
+
}
|
|
69
|
+
const result = {
|
|
70
|
+
key: uploadUrl.key,
|
|
71
|
+
type: isVideo ? 'VIDEO' : 'IMAGE',
|
|
72
|
+
contentType,
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
content: [
|
|
76
|
+
{ type: 'text', text: JSON.stringify(result, null, 2) },
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/tools/files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const PLATFORMS = [
|
|
3
|
+
'FACEBOOK',
|
|
4
|
+
'INSTAGRAM',
|
|
5
|
+
'X',
|
|
6
|
+
'TIKTOK',
|
|
7
|
+
'LINKEDIN',
|
|
8
|
+
'YOUTUBE',
|
|
9
|
+
'BLUESKY',
|
|
10
|
+
'THREADS',
|
|
11
|
+
'PINTEREST',
|
|
12
|
+
'TELEGRAM',
|
|
13
|
+
];
|
|
14
|
+
const STATUSES = ['DRAFT', 'SCHEDULED', 'PUBLISHED', 'FAILED'];
|
|
15
|
+
export function registerPostTools(server, client) {
|
|
16
|
+
server.tool('list_posts', 'List social media posts with optional filters for platform, status, and date range', {
|
|
17
|
+
page: z.number().int().min(0).default(0).describe('Page number (0-based)'),
|
|
18
|
+
limit: z
|
|
19
|
+
.number()
|
|
20
|
+
.int()
|
|
21
|
+
.min(1)
|
|
22
|
+
.max(50)
|
|
23
|
+
.default(20)
|
|
24
|
+
.describe('Posts per page (max 50)'),
|
|
25
|
+
platforms: z
|
|
26
|
+
.array(z.enum(PLATFORMS))
|
|
27
|
+
.optional()
|
|
28
|
+
.describe('Filter by platforms'),
|
|
29
|
+
statuses: z
|
|
30
|
+
.array(z.enum(STATUSES))
|
|
31
|
+
.optional()
|
|
32
|
+
.describe('Filter by post statuses'),
|
|
33
|
+
from: z
|
|
34
|
+
.string()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe('Start date filter (ISO 8601, e.g. 2025-01-01T00:00:00.000Z)'),
|
|
37
|
+
to: z
|
|
38
|
+
.string()
|
|
39
|
+
.optional()
|
|
40
|
+
.describe('End date filter (ISO 8601, e.g. 2025-01-31T23:59:59.999Z)'),
|
|
41
|
+
}, async (input) => {
|
|
42
|
+
const data = await client.get('/social-posts', {
|
|
43
|
+
page: String(input.page),
|
|
44
|
+
limit: String(input.limit),
|
|
45
|
+
platforms: input.platforms?.join(','),
|
|
46
|
+
statuses: input.statuses?.join(','),
|
|
47
|
+
from: input.from,
|
|
48
|
+
to: input.to,
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
server.tool('create_posts', 'Create and schedule social media posts. Supports batch creation (up to 15 posts). Each post targets a specific social account.', {
|
|
55
|
+
posts: z
|
|
56
|
+
.array(z.object({
|
|
57
|
+
content: z.string().describe('Post text content'),
|
|
58
|
+
firstComment: z.string().optional().describe('First comment to add after publishing'),
|
|
59
|
+
mediaItems: z
|
|
60
|
+
.array(z.object({
|
|
61
|
+
key: z.string().describe('S3 media key from get_upload_urls'),
|
|
62
|
+
type: z.enum(['IMAGE', 'VIDEO']),
|
|
63
|
+
sortOrder: z.number().int().min(0),
|
|
64
|
+
coverTimestamp: z.string().optional().describe('Video cover timestamp'),
|
|
65
|
+
}))
|
|
66
|
+
.optional()
|
|
67
|
+
.describe('Media attachments'),
|
|
68
|
+
scheduledAt: z
|
|
69
|
+
.string()
|
|
70
|
+
.optional()
|
|
71
|
+
.describe('Schedule time (ISO 8601). Required when status is SCHEDULED.'),
|
|
72
|
+
socialMediaId: z
|
|
73
|
+
.string()
|
|
74
|
+
.uuid()
|
|
75
|
+
.describe('Target social account ID (from list_accounts)'),
|
|
76
|
+
}))
|
|
77
|
+
.min(1)
|
|
78
|
+
.max(15)
|
|
79
|
+
.describe('Array of posts to create'),
|
|
80
|
+
status: z
|
|
81
|
+
.enum(['DRAFT', 'SCHEDULED'])
|
|
82
|
+
.default('SCHEDULED')
|
|
83
|
+
.describe('Post status. SCHEDULED requires scheduledAt on all posts.'),
|
|
84
|
+
approvalStatus: z
|
|
85
|
+
.enum(['PENDING_APPROVAL', 'APPROVED'])
|
|
86
|
+
.default('APPROVED')
|
|
87
|
+
.describe('Approval workflow status'),
|
|
88
|
+
controls: z
|
|
89
|
+
.object({
|
|
90
|
+
// X/Twitter
|
|
91
|
+
xCommunityId: z.string().optional(),
|
|
92
|
+
xQuoteTweetUrl: z.string().optional(),
|
|
93
|
+
xRetweetUrl: z.string().optional(),
|
|
94
|
+
// TikTok
|
|
95
|
+
tiktokPrivacy: z.enum(['PUBLIC', 'MUTUAL_FRIENDS', 'ONLY_ME']).optional(),
|
|
96
|
+
tiktokIsDraft: z.boolean().optional(),
|
|
97
|
+
tiktokAllowComments: z.boolean().optional(),
|
|
98
|
+
tiktokAllowDuet: z.boolean().optional(),
|
|
99
|
+
tiktokAllowStitch: z.boolean().optional(),
|
|
100
|
+
tiktokBrandOrganic: z.boolean().optional(),
|
|
101
|
+
tiktokBrandContent: z.boolean().optional(),
|
|
102
|
+
tiktokAutoAddMusic: z.boolean().optional(),
|
|
103
|
+
// Instagram
|
|
104
|
+
instagramPostToGrid: z.boolean().optional(),
|
|
105
|
+
instagramPublishType: z.enum(['TIMELINE', 'STORY', 'REEL']).optional(),
|
|
106
|
+
instagramCollaborators: z.array(z.string()).optional(),
|
|
107
|
+
// YouTube
|
|
108
|
+
youtubePrivacy: z.enum(['PUBLIC', 'PRIVATE', 'UNLISTED']).optional(),
|
|
109
|
+
youtubeTags: z.array(z.string()).optional(),
|
|
110
|
+
youtubeCategoryId: z.string().optional(),
|
|
111
|
+
youtubeIsShort: z.boolean().optional(),
|
|
112
|
+
youtubeMadeForKids: z.boolean().optional(),
|
|
113
|
+
youtubeTitle: z.string().optional(),
|
|
114
|
+
youtubePlaylistId: z.string().optional(),
|
|
115
|
+
// Facebook
|
|
116
|
+
facebookContentType: z.enum(['POST', 'REEL', 'STORY']).optional(),
|
|
117
|
+
facebookAllowComments: z.boolean().optional(),
|
|
118
|
+
facebookPrivacy: z
|
|
119
|
+
.enum(['PUBLIC', 'FRIENDS_OF_FRIENDS', 'FRIENDS', 'SELF'])
|
|
120
|
+
.optional(),
|
|
121
|
+
facebookCarouselMainLink: z.string().optional(),
|
|
122
|
+
facebookCarouselShowEndCard: z.boolean().optional(),
|
|
123
|
+
facebookReelsCoverImageKey: z.string().optional(),
|
|
124
|
+
facebookReelsCollaborators: z.array(z.string()).optional(),
|
|
125
|
+
// Pinterest
|
|
126
|
+
pinterestBoardId: z.string().optional(),
|
|
127
|
+
pinterestLink: z.string().optional(),
|
|
128
|
+
// LinkedIn
|
|
129
|
+
linkedinAttachmentKey: z.string().optional(),
|
|
130
|
+
linkedinAttachmentTitle: z.string().optional(),
|
|
131
|
+
})
|
|
132
|
+
.optional()
|
|
133
|
+
.describe('Platform-specific controls (shared across all posts in the batch)'),
|
|
134
|
+
}, async (input) => {
|
|
135
|
+
const data = await client.post('/social-posts', {
|
|
136
|
+
posts: input.posts,
|
|
137
|
+
status: input.status,
|
|
138
|
+
approvalStatus: input.approvalStatus,
|
|
139
|
+
controls: input.controls,
|
|
140
|
+
});
|
|
141
|
+
return {
|
|
142
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
server.tool('delete_post', 'Delete a social media post by ID', {
|
|
146
|
+
id: z.string().uuid().describe('Post ID to delete'),
|
|
147
|
+
}, async (input) => {
|
|
148
|
+
const data = await client.delete(`/social-posts/${input.id}`);
|
|
149
|
+
return {
|
|
150
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
151
|
+
};
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=posts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"posts.js","sourceRoot":"","sources":["../../src/tools/posts.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export type Platform = 'FACEBOOK' | 'INSTAGRAM' | 'X' | 'TIKTOK' | 'LINKEDIN' | 'YOUTUBE' | 'BLUESKY' | 'THREADS' | 'PINTEREST' | 'TELEGRAM';
|
|
2
|
+
export type PostStatus = 'DRAFT' | 'SCHEDULED' | 'PUBLISHED' | 'FAILED';
|
|
3
|
+
export type ApprovalStatus = 'PENDING_APPROVAL' | 'IN_PROGRESS' | 'APPROVED' | 'REJECTED' | 'NEEDS_WORK';
|
|
4
|
+
export interface SocialPost {
|
|
5
|
+
id: string;
|
|
6
|
+
content: string;
|
|
7
|
+
status: PostStatus;
|
|
8
|
+
approvalStatus: ApprovalStatus;
|
|
9
|
+
socialMediaId: string;
|
|
10
|
+
mediaItems: MediaItem[];
|
|
11
|
+
scheduledAt: string | null;
|
|
12
|
+
publishedAt: string | null;
|
|
13
|
+
failedAt: string | null;
|
|
14
|
+
platformPostId: string | null;
|
|
15
|
+
groupId: string | null;
|
|
16
|
+
firstComment: string | null;
|
|
17
|
+
firstCommentError: string | null;
|
|
18
|
+
lastError: {
|
|
19
|
+
message: string;
|
|
20
|
+
code: string | null;
|
|
21
|
+
} | null;
|
|
22
|
+
}
|
|
23
|
+
export interface MediaItem {
|
|
24
|
+
key: string;
|
|
25
|
+
type: 'IMAGE' | 'VIDEO';
|
|
26
|
+
sortOrder: number;
|
|
27
|
+
url?: string;
|
|
28
|
+
coverTimestamp?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface PaginatedPosts {
|
|
31
|
+
data: SocialPost[];
|
|
32
|
+
totalCount: number;
|
|
33
|
+
pageInfo: {
|
|
34
|
+
page: number;
|
|
35
|
+
hasNextPage: boolean;
|
|
36
|
+
perPage: number;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export interface SocialAccount {
|
|
40
|
+
id: string;
|
|
41
|
+
platform: Platform;
|
|
42
|
+
platformUsername: string | null;
|
|
43
|
+
displayName: string | null;
|
|
44
|
+
}
|
|
45
|
+
export interface PinterestBoard {
|
|
46
|
+
id: string;
|
|
47
|
+
boardId: string;
|
|
48
|
+
name: string;
|
|
49
|
+
description: string | null;
|
|
50
|
+
imageUrl: string | null;
|
|
51
|
+
}
|
|
52
|
+
export interface YouTubePlaylist {
|
|
53
|
+
id: string;
|
|
54
|
+
playlistId: string;
|
|
55
|
+
title: string;
|
|
56
|
+
description: string | null;
|
|
57
|
+
thumbnailUrl: string | null;
|
|
58
|
+
}
|
|
59
|
+
export interface SignedUploadUrl {
|
|
60
|
+
key: string;
|
|
61
|
+
signedUrl: string;
|
|
62
|
+
}
|
|
63
|
+
export interface CreatePostInput {
|
|
64
|
+
content: string;
|
|
65
|
+
firstComment?: string;
|
|
66
|
+
mediaItems?: MediaItem[];
|
|
67
|
+
scheduledAt?: string;
|
|
68
|
+
socialMediaId: string;
|
|
69
|
+
}
|
|
70
|
+
export interface PostControls {
|
|
71
|
+
xCommunityId?: string;
|
|
72
|
+
xQuoteTweetUrl?: string;
|
|
73
|
+
xRetweetUrl?: string;
|
|
74
|
+
tiktokPrivacy?: 'PUBLIC' | 'MUTUAL_FRIENDS' | 'ONLY_ME';
|
|
75
|
+
tiktokIsDraft?: boolean;
|
|
76
|
+
tiktokAllowComments?: boolean;
|
|
77
|
+
tiktokAllowDuet?: boolean;
|
|
78
|
+
tiktokAllowStitch?: boolean;
|
|
79
|
+
tiktokBrandOrganic?: boolean;
|
|
80
|
+
tiktokBrandContent?: boolean;
|
|
81
|
+
tiktokAutoAddMusic?: boolean;
|
|
82
|
+
instagramPostToGrid?: boolean;
|
|
83
|
+
instagramPublishType?: 'TIMELINE' | 'STORY' | 'REEL';
|
|
84
|
+
instagramCollaborators?: string[];
|
|
85
|
+
youtubePrivacy?: 'PUBLIC' | 'PRIVATE' | 'UNLISTED';
|
|
86
|
+
youtubeTags?: string[];
|
|
87
|
+
youtubeCategoryId?: string;
|
|
88
|
+
youtubeIsShort?: boolean;
|
|
89
|
+
youtubeMadeForKids?: boolean;
|
|
90
|
+
youtubeTitle?: string;
|
|
91
|
+
youtubePlaylistId?: string;
|
|
92
|
+
facebookContentType?: 'POST' | 'REEL' | 'STORY';
|
|
93
|
+
facebookAllowComments?: boolean;
|
|
94
|
+
facebookPrivacy?: 'PUBLIC' | 'FRIENDS_OF_FRIENDS' | 'FRIENDS' | 'SELF';
|
|
95
|
+
facebookCarouselMainLink?: string;
|
|
96
|
+
facebookCarouselShowEndCard?: boolean;
|
|
97
|
+
facebookReelsCoverImageKey?: string;
|
|
98
|
+
facebookReelsCollaborators?: string[];
|
|
99
|
+
pinterestBoardId?: string;
|
|
100
|
+
pinterestLink?: string;
|
|
101
|
+
linkedinAttachmentKey?: string;
|
|
102
|
+
linkedinAttachmentTitle?: string;
|
|
103
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "postfast-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for the PostFast API — schedule and manage social media posts via AI tools",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"postfast-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"start": "node dist/index.js",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"mcp",
|
|
22
|
+
"postfast",
|
|
23
|
+
"social-media",
|
|
24
|
+
"scheduling",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"claude",
|
|
27
|
+
"ai"
|
|
28
|
+
],
|
|
29
|
+
"author": "PostFast",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/peturgeorgievv-factory/postfast-mcp"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.12.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"typescript": "^5.7.0",
|
|
43
|
+
"@types/node": "^22.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|