posterly-mcp-server 0.45.1 → 0.45.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/RELEASING.md +12 -0
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/tools/audit-google-business-profile.d.ts +4 -0
- package/dist/tools/audit-google-business-profile.js +32 -15
- package/dist/tools/create-post.js +9 -0
- package/dist/tools/update-post.js +3 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/lib/version.ts +1 -1
- package/src/tools/audit-google-business-profile.ts +34 -15
- package/src/tools/create-post.ts +9 -0
- package/src/tools/update-post.ts +3 -0
package/README.md
CHANGED
|
@@ -122,7 +122,7 @@ Add the same server definition to your Cursor MCP settings:
|
|
|
122
122
|
|
|
123
123
|
## Available tools
|
|
124
124
|
|
|
125
|
-
`posterly-mcp-server@0.45.
|
|
125
|
+
`posterly-mcp-server@0.45.3` exposes 62 tools covering 94 actions. The hosted HTTP endpoint exposes the same catalog minus the three signup tools: 59 tools covering 91 actions.
|
|
126
126
|
|
|
127
127
|
Many tools pick what to do with one parameter: `view` (reads), `action` (writes), or `type`. A few pick their mode from which parameters you pass (for example `update_post` with `status` changes the status instead of editing). Every tool description names all of its views and actions.
|
|
128
128
|
|
package/RELEASING.md
CHANGED
|
@@ -101,3 +101,15 @@ up on their next client restart; there is nothing for them to do.
|
|
|
101
101
|
Do not `npm unpublish` and do not republish the same number — npm versions are
|
|
102
102
|
immutable and unpublishing breaks anyone who already resolved it. Cut the next
|
|
103
103
|
patch version and publish that.
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
## Publishing from GitHub (trusted publishing)
|
|
107
|
+
|
|
108
|
+
npm now requires 2FA or a trusted publisher to publish. The repo has a manual
|
|
109
|
+
workflow, `.github/workflows/publish-mcp.yml` ("Publish MCP"), that publishes
|
|
110
|
+
`mcp-server/` with GitHub OIDC, so no npm token or one-time code is needed.
|
|
111
|
+
One-time setup on npmjs.com: package `posterly-mcp-server` > Settings >
|
|
112
|
+
Trusted Publisher > GitHub Actions, owner `awpthorp`, repository `posterly`,
|
|
113
|
+
workflow `publish-mcp.yml`. After an MCP change merges and deploys, run it from
|
|
114
|
+
the Actions tab (or `gh workflow run publish-mcp.yml`), then
|
|
115
|
+
`npm run check:mcp-published`. It skips if the version is already on npm.
|
package/dist/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const POSTERLY_MCP_VERSION = "0.45.
|
|
1
|
+
export declare const POSTERLY_MCP_VERSION = "0.45.3";
|
package/dist/lib/version.js
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
// tool set (minus the intentional pre-auth signup tools that only this stdio
|
|
6
6
|
// package exposes). `npm run check:mcp-parity` enforces both the version match
|
|
7
7
|
// and the tool-list match, and runs in the pre-commit hook.
|
|
8
|
-
export const POSTERLY_MCP_VERSION = '0.45.
|
|
8
|
+
export const POSTERLY_MCP_VERSION = '0.45.3';
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
export declare function formatGoogleBusinessAuditText(result: {
|
|
4
|
+
audit?: any;
|
|
5
|
+
fetchedAt?: string;
|
|
6
|
+
}): string;
|
|
3
7
|
export declare const auditGoogleBusinessProfileTool: {
|
|
4
8
|
name: string;
|
|
5
9
|
description: string;
|
|
@@ -1,7 +1,37 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
const AUDIT_SECTION_ORDER = ['basicInfo', 'hours', 'categories', 'description', 'media', 'attributes', 'reviews'];
|
|
3
|
+
export function formatGoogleBusinessAuditText(result) {
|
|
4
|
+
const audit = result.audit || {};
|
|
5
|
+
const recommendations = Array.isArray(audit.recommendations) ? audit.recommendations : [];
|
|
6
|
+
const sections = audit.sections && typeof audit.sections === 'object' ? audit.sections : {};
|
|
7
|
+
const lines = [
|
|
8
|
+
`Google Business audit: ${audit.businessName || 'Unknown business'}`,
|
|
9
|
+
`Score: ${audit.overallScore ?? 'n/a'} (${audit.overallGrade || 'n/a'})`,
|
|
10
|
+
`Fetched: ${result.fetchedAt || 'unknown'}`,
|
|
11
|
+
];
|
|
12
|
+
const keys = [
|
|
13
|
+
...AUDIT_SECTION_ORDER.filter((key) => sections[key]),
|
|
14
|
+
...Object.keys(sections).filter((key) => !AUDIT_SECTION_ORDER.includes(key)),
|
|
15
|
+
];
|
|
16
|
+
if (keys.length > 0) {
|
|
17
|
+
lines.push('Sections:');
|
|
18
|
+
for (const key of keys) {
|
|
19
|
+
const section = sections[key] || {};
|
|
20
|
+
const label = section.label || key;
|
|
21
|
+
lines.push(`- ${label}: ${section.score ?? 'n/a'}/${section.maxScore ?? 'n/a'}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (recommendations.length > 0) {
|
|
25
|
+
lines.push('Top recommendations:');
|
|
26
|
+
for (const rec of recommendations.slice(0, 5)) {
|
|
27
|
+
lines.push(`- ${rec.priority || 'medium'}: ${rec.description || rec.title || 'Recommendation'}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return lines.join('\n');
|
|
31
|
+
}
|
|
2
32
|
export const auditGoogleBusinessProfileTool = {
|
|
3
33
|
name: 'audit_google_business_profile',
|
|
4
|
-
description: 'Run a live local-profile audit for a connected Google Business Profile location
|
|
34
|
+
description: 'Run a live local-profile audit for a connected Google Business Profile location. Returns the 0-100 completeness score, letter grade, a score for each section (profile basics, hours, categories, description, photos, attributes, review health), and recommendations.',
|
|
5
35
|
inputSchema: z.object({
|
|
6
36
|
workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
|
|
7
37
|
account_id: z.number().int().positive().optional().describe('Google Business account ID from list_accounts.'),
|
|
@@ -9,19 +39,6 @@ export const auditGoogleBusinessProfileTool = {
|
|
|
9
39
|
}),
|
|
10
40
|
async execute(client, input) {
|
|
11
41
|
const result = await client.auditGoogleBusinessProfile(input);
|
|
12
|
-
|
|
13
|
-
const recommendations = Array.isArray(audit.recommendations) ? audit.recommendations : [];
|
|
14
|
-
const lines = [
|
|
15
|
-
`Google Business audit: ${audit.businessName || 'Unknown business'}`,
|
|
16
|
-
`Score: ${audit.overallScore ?? 'n/a'} (${audit.overallGrade || 'n/a'})`,
|
|
17
|
-
`Fetched: ${result.fetchedAt || 'unknown'}`,
|
|
18
|
-
];
|
|
19
|
-
if (recommendations.length > 0) {
|
|
20
|
-
lines.push('Top recommendations:');
|
|
21
|
-
for (const rec of recommendations.slice(0, 5)) {
|
|
22
|
-
lines.push(`- ${rec.priority || 'medium'}: ${rec.description || rec.title || 'Recommendation'}`);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return lines.join('\n');
|
|
42
|
+
return formatGoogleBusinessAuditText(result);
|
|
26
43
|
},
|
|
27
44
|
};
|
|
@@ -310,6 +310,15 @@ export const createPostTool = {
|
|
|
310
310
|
`• Scheduled: ${when}`,
|
|
311
311
|
`• View in posterly: ${dashboardUrl}`,
|
|
312
312
|
];
|
|
313
|
+
if (p.reel_cover_url) {
|
|
314
|
+
lines.push(`• Reel cover: ${p.reel_cover_url}`);
|
|
315
|
+
}
|
|
316
|
+
if (p.thumbnail_url) {
|
|
317
|
+
lines.push(`• Thumbnail: ${p.thumbnail_url}`);
|
|
318
|
+
}
|
|
319
|
+
if (p.youtube_thumbnail_url) {
|
|
320
|
+
lines.push(`• YouTube thumbnail: ${p.youtube_thumbnail_url}`);
|
|
321
|
+
}
|
|
313
322
|
if (thread_posts) {
|
|
314
323
|
lines.push(`• Thread: ${thread_posts.length} posts`);
|
|
315
324
|
}
|
|
@@ -191,6 +191,9 @@ async function editPost(client, input) {
|
|
|
191
191
|
['Status', statusLabel(p.status)],
|
|
192
192
|
['Scheduled', formatDateTime(p.scheduled_at)],
|
|
193
193
|
['Type', p.post_type || 'unknown'],
|
|
194
|
+
...(p.reel_cover_url ? [['Reel cover', p.reel_cover_url]] : []),
|
|
195
|
+
...(p.thumbnail_url ? [['Thumbnail', p.thumbnail_url]] : []),
|
|
196
|
+
...(p.youtube_thumbnail_url ? [['YouTube thumbnail', p.youtube_thumbnail_url]] : []),
|
|
194
197
|
]),
|
|
195
198
|
mdSection('Caption', p.content ? mdQuote(p.content) : '_(empty)_'),
|
|
196
199
|
].join('\n\n');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "posterly-mcp-server",
|
|
3
|
-
"version": "0.45.
|
|
3
|
+
"version": "0.45.3",
|
|
4
4
|
"mcpName": "io.github.awpthorp/posterly",
|
|
5
5
|
"description": "MCP server for posterly: schedule and publish social media posts across 18 platforms from any MCP client (Claude, ChatGPT, Cursor, Windsurf, Cline, and more)",
|
|
6
6
|
"license": "MIT",
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.awpthorp/posterly",
|
|
4
4
|
"title": "posterly",
|
|
5
5
|
"description": "Validate, schedule, publish, and analyze social content across 18 platforms with posterly.",
|
|
6
|
-
"version": "0.45.
|
|
6
|
+
"version": "0.45.3",
|
|
7
7
|
"websiteUrl": "https://www.poster.ly/mcp",
|
|
8
8
|
"repository": {
|
|
9
9
|
"url": "https://github.com/awpthorp/posterly",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
{
|
|
15
15
|
"registryType": "npm",
|
|
16
16
|
"identifier": "posterly-mcp-server",
|
|
17
|
-
"version": "0.45.
|
|
17
|
+
"version": "0.45.3",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
|
20
20
|
},
|
package/src/lib/version.ts
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
// tool set (minus the intentional pre-auth signup tools that only this stdio
|
|
6
6
|
// package exposes). `npm run check:mcp-parity` enforces both the version match
|
|
7
7
|
// and the tool-list match, and runs in the pre-commit hook.
|
|
8
|
-
export const POSTERLY_MCP_VERSION = '0.45.
|
|
8
|
+
export const POSTERLY_MCP_VERSION = '0.45.3';
|
|
@@ -1,10 +1,42 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
3
|
|
|
4
|
+
const AUDIT_SECTION_ORDER = ['basicInfo', 'hours', 'categories', 'description', 'media', 'attributes', 'reviews'];
|
|
5
|
+
|
|
6
|
+
export function formatGoogleBusinessAuditText(result: { audit?: any; fetchedAt?: string }): string {
|
|
7
|
+
const audit = result.audit || {};
|
|
8
|
+
const recommendations = Array.isArray(audit.recommendations) ? audit.recommendations : [];
|
|
9
|
+
const sections = audit.sections && typeof audit.sections === 'object' ? audit.sections : {};
|
|
10
|
+
const lines = [
|
|
11
|
+
`Google Business audit: ${audit.businessName || 'Unknown business'}`,
|
|
12
|
+
`Score: ${audit.overallScore ?? 'n/a'} (${audit.overallGrade || 'n/a'})`,
|
|
13
|
+
`Fetched: ${result.fetchedAt || 'unknown'}`,
|
|
14
|
+
];
|
|
15
|
+
const keys = [
|
|
16
|
+
...AUDIT_SECTION_ORDER.filter((key) => sections[key]),
|
|
17
|
+
...Object.keys(sections).filter((key) => !AUDIT_SECTION_ORDER.includes(key)),
|
|
18
|
+
];
|
|
19
|
+
if (keys.length > 0) {
|
|
20
|
+
lines.push('Sections:');
|
|
21
|
+
for (const key of keys) {
|
|
22
|
+
const section = sections[key] || {};
|
|
23
|
+
const label = section.label || key;
|
|
24
|
+
lines.push(`- ${label}: ${section.score ?? 'n/a'}/${section.maxScore ?? 'n/a'}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (recommendations.length > 0) {
|
|
28
|
+
lines.push('Top recommendations:');
|
|
29
|
+
for (const rec of recommendations.slice(0, 5)) {
|
|
30
|
+
lines.push(`- ${rec.priority || 'medium'}: ${rec.description || rec.title || 'Recommendation'}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return lines.join('\n');
|
|
34
|
+
}
|
|
35
|
+
|
|
4
36
|
export const auditGoogleBusinessProfileTool = {
|
|
5
37
|
name: 'audit_google_business_profile',
|
|
6
38
|
description:
|
|
7
|
-
'Run a live local-profile audit for a connected Google Business Profile location
|
|
39
|
+
'Run a live local-profile audit for a connected Google Business Profile location. Returns the 0-100 completeness score, letter grade, a score for each section (profile basics, hours, categories, description, photos, attributes, review health), and recommendations.',
|
|
8
40
|
inputSchema: z.object({
|
|
9
41
|
workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
|
|
10
42
|
account_id: z.number().int().positive().optional().describe('Google Business account ID from list_accounts.'),
|
|
@@ -20,19 +52,6 @@ export const auditGoogleBusinessProfileTool = {
|
|
|
20
52
|
},
|
|
21
53
|
) {
|
|
22
54
|
const result = await client.auditGoogleBusinessProfile(input);
|
|
23
|
-
|
|
24
|
-
const recommendations = Array.isArray(audit.recommendations) ? audit.recommendations : [];
|
|
25
|
-
const lines = [
|
|
26
|
-
`Google Business audit: ${audit.businessName || 'Unknown business'}`,
|
|
27
|
-
`Score: ${audit.overallScore ?? 'n/a'} (${audit.overallGrade || 'n/a'})`,
|
|
28
|
-
`Fetched: ${result.fetchedAt || 'unknown'}`,
|
|
29
|
-
];
|
|
30
|
-
if (recommendations.length > 0) {
|
|
31
|
-
lines.push('Top recommendations:');
|
|
32
|
-
for (const rec of recommendations.slice(0, 5)) {
|
|
33
|
-
lines.push(`- ${rec.priority || 'medium'}: ${rec.description || rec.title || 'Recommendation'}`);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return lines.join('\n');
|
|
55
|
+
return formatGoogleBusinessAuditText(result);
|
|
37
56
|
},
|
|
38
57
|
};
|
package/src/tools/create-post.ts
CHANGED
|
@@ -345,6 +345,15 @@ export const createPostTool = {
|
|
|
345
345
|
`• Scheduled: ${when}`,
|
|
346
346
|
`• View in posterly: ${dashboardUrl}`,
|
|
347
347
|
];
|
|
348
|
+
if (p.reel_cover_url) {
|
|
349
|
+
lines.push(`• Reel cover: ${p.reel_cover_url}`);
|
|
350
|
+
}
|
|
351
|
+
if (p.thumbnail_url) {
|
|
352
|
+
lines.push(`• Thumbnail: ${p.thumbnail_url}`);
|
|
353
|
+
}
|
|
354
|
+
if (p.youtube_thumbnail_url) {
|
|
355
|
+
lines.push(`• YouTube thumbnail: ${p.youtube_thumbnail_url}`);
|
|
356
|
+
}
|
|
348
357
|
if (thread_posts) {
|
|
349
358
|
lines.push(`• Thread: ${thread_posts.length} posts`);
|
|
350
359
|
}
|
package/src/tools/update-post.ts
CHANGED
|
@@ -217,6 +217,9 @@ async function editPost(client: PosterlyClient, input: EditInput): Promise<strin
|
|
|
217
217
|
['Status', statusLabel(p.status)],
|
|
218
218
|
['Scheduled', formatDateTime(p.scheduled_at)],
|
|
219
219
|
['Type', p.post_type || 'unknown'],
|
|
220
|
+
...(p.reel_cover_url ? [['Reel cover', p.reel_cover_url] as [string, string]] : []),
|
|
221
|
+
...(p.thumbnail_url ? [['Thumbnail', p.thumbnail_url] as [string, string]] : []),
|
|
222
|
+
...(p.youtube_thumbnail_url ? [['YouTube thumbnail', p.youtube_thumbnail_url] as [string, string]] : []),
|
|
220
223
|
]),
|
|
221
224
|
mdSection('Caption', p.content ? mdQuote(p.content) : '_(empty)_'),
|
|
222
225
|
].join('\n\n');
|