posterly-mcp-server 0.22.0 → 0.23.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 +4 -1
- package/dist/index.js +30 -0
- package/dist/lib/api-client.d.ts +41 -0
- package/dist/lib/api-client.js +17 -0
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/tools/add-google-business-media.d.ts +42 -0
- package/dist/tools/add-google-business-media.js +34 -0
- package/dist/tools/delete-google-business-media.d.ts +32 -0
- package/dist/tools/delete-google-business-media.js +17 -0
- package/dist/tools/list-google-business-media.d.ts +24 -0
- package/dist/tools/list-google-business-media.js +25 -0
- package/package.json +1 -1
- package/src/index.ts +45 -0
- package/src/lib/api-client.ts +53 -0
- package/src/lib/version.ts +1 -1
- package/src/tools/add-google-business-media.ts +50 -0
- package/src/tools/delete-google-business-media.ts +30 -0
- package/src/tools/list-google-business-media.ts +33 -0
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ Add the same server definition to your Cursor MCP settings:
|
|
|
108
108
|
|
|
109
109
|
## Available tools
|
|
110
110
|
|
|
111
|
-
`posterly-mcp-server@0.
|
|
111
|
+
`posterly-mcp-server@0.23.0` exposes 62 tools.
|
|
112
112
|
|
|
113
113
|
Public setup tools work before `POSTERLY_API_KEY` exists:
|
|
114
114
|
|
|
@@ -167,6 +167,9 @@ Authenticated tools require `POSTERLY_API_KEY`:
|
|
|
167
167
|
- `suggest_google_business_review_reply`
|
|
168
168
|
- `reply_google_business_review` (post/update a public GBP review reply after explicit confirmation)
|
|
169
169
|
- `delete_google_business_review_reply` (delete a GBP review reply after explicit confirmation)
|
|
170
|
+
- `list_google_business_media` (list the photos/videos on a GBP profile gallery)
|
|
171
|
+
- `add_google_business_media` (add a photo/video to a GBP profile gallery from a public URL, after explicit confirmation)
|
|
172
|
+
- `delete_google_business_media` (remove a photo/video from a GBP profile gallery after explicit confirmation)
|
|
170
173
|
- `list_activity`
|
|
171
174
|
- `list_webhooks`
|
|
172
175
|
- `create_webhook` (create a webhook after explicit confirmation)
|
package/dist/index.js
CHANGED
|
@@ -41,6 +41,9 @@ import { auditGoogleBusinessProfileTool } from './tools/audit-google-business-pr
|
|
|
41
41
|
import { suggestGoogleBusinessReviewReplyTool } from './tools/suggest-google-business-review-reply.js';
|
|
42
42
|
import { replyGoogleBusinessReviewTool } from './tools/reply-google-business-review.js';
|
|
43
43
|
import { deleteGoogleBusinessReviewReplyTool } from './tools/delete-google-business-review-reply.js';
|
|
44
|
+
import { listGoogleBusinessMediaTool } from './tools/list-google-business-media.js';
|
|
45
|
+
import { addGoogleBusinessMediaTool } from './tools/add-google-business-media.js';
|
|
46
|
+
import { deleteGoogleBusinessMediaTool } from './tools/delete-google-business-media.js';
|
|
44
47
|
import { listActivityTool } from './tools/list-activity.js';
|
|
45
48
|
import { listWebhooksTool } from './tools/list-webhooks.js';
|
|
46
49
|
import { createWebhookTool } from './tools/create-webhook.js';
|
|
@@ -536,6 +539,33 @@ server.tool(deleteGoogleBusinessReviewReplyTool.name, deleteGoogleBusinessReview
|
|
|
536
539
|
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
537
540
|
}
|
|
538
541
|
});
|
|
542
|
+
server.tool(listGoogleBusinessMediaTool.name, listGoogleBusinessMediaTool.description, listGoogleBusinessMediaTool.inputSchema.shape, async (input) => {
|
|
543
|
+
try {
|
|
544
|
+
const text = await listGoogleBusinessMediaTool.execute(client, input);
|
|
545
|
+
return { content: [{ type: 'text', text }] };
|
|
546
|
+
}
|
|
547
|
+
catch (err) {
|
|
548
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
server.tool(addGoogleBusinessMediaTool.name, addGoogleBusinessMediaTool.description, addGoogleBusinessMediaTool.inputSchema.shape, async (input) => {
|
|
552
|
+
try {
|
|
553
|
+
const text = await addGoogleBusinessMediaTool.execute(client, input);
|
|
554
|
+
return { content: [{ type: 'text', text }] };
|
|
555
|
+
}
|
|
556
|
+
catch (err) {
|
|
557
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
server.tool(deleteGoogleBusinessMediaTool.name, deleteGoogleBusinessMediaTool.description, deleteGoogleBusinessMediaTool.inputSchema.shape, async (input) => {
|
|
561
|
+
try {
|
|
562
|
+
const text = await deleteGoogleBusinessMediaTool.execute(client, input);
|
|
563
|
+
return { content: [{ type: 'text', text }] };
|
|
564
|
+
}
|
|
565
|
+
catch (err) {
|
|
566
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
567
|
+
}
|
|
568
|
+
});
|
|
539
569
|
server.tool(listActivityTool.name, listActivityTool.description, listActivityTool.inputSchema.shape, async (input) => {
|
|
540
570
|
try {
|
|
541
571
|
const text = await listActivityTool.execute(client, input);
|
package/dist/lib/api-client.d.ts
CHANGED
|
@@ -372,6 +372,28 @@ export interface GoogleBusinessReviewsResponse {
|
|
|
372
372
|
total: number;
|
|
373
373
|
locations: Array<Record<string, unknown>>;
|
|
374
374
|
}
|
|
375
|
+
export interface GoogleBusinessMediaItem {
|
|
376
|
+
name: string;
|
|
377
|
+
mediaFormat: 'PHOTO' | 'VIDEO' | 'MEDIA_FORMAT_UNSPECIFIED';
|
|
378
|
+
category: string | null;
|
|
379
|
+
googleUrl: string | null;
|
|
380
|
+
thumbnailUrl: string | null;
|
|
381
|
+
createTime: string | null;
|
|
382
|
+
widthPixels: number | null;
|
|
383
|
+
heightPixels: number | null;
|
|
384
|
+
attribution: {
|
|
385
|
+
profileName?: string;
|
|
386
|
+
profilePhotoUrl?: string;
|
|
387
|
+
profileUrl?: string;
|
|
388
|
+
} | null;
|
|
389
|
+
account_id?: number;
|
|
390
|
+
location_id?: string | null;
|
|
391
|
+
}
|
|
392
|
+
export interface GoogleBusinessMediaResponse {
|
|
393
|
+
media: GoogleBusinessMediaItem[];
|
|
394
|
+
total: number;
|
|
395
|
+
locations: Array<Record<string, unknown>>;
|
|
396
|
+
}
|
|
375
397
|
export interface GoogleBusinessReviewLinkResponse {
|
|
376
398
|
reviewLink: string;
|
|
377
399
|
placeId: string;
|
|
@@ -820,6 +842,25 @@ export declare class PosterlyClient {
|
|
|
820
842
|
location_id?: string;
|
|
821
843
|
review_name: string;
|
|
822
844
|
}): Promise<Record<string, any>>;
|
|
845
|
+
listGoogleBusinessMedia(params?: {
|
|
846
|
+
workspace_id?: string;
|
|
847
|
+
account_id?: number;
|
|
848
|
+
location_id?: string;
|
|
849
|
+
}): Promise<GoogleBusinessMediaResponse>;
|
|
850
|
+
addGoogleBusinessMedia(data: {
|
|
851
|
+
workspace_id?: string;
|
|
852
|
+
account_id?: number;
|
|
853
|
+
location_id?: string;
|
|
854
|
+
source_url: string;
|
|
855
|
+
category: string;
|
|
856
|
+
media_format?: 'PHOTO' | 'VIDEO';
|
|
857
|
+
}): Promise<Record<string, any>>;
|
|
858
|
+
deleteGoogleBusinessMedia(data: {
|
|
859
|
+
workspace_id?: string;
|
|
860
|
+
account_id?: number;
|
|
861
|
+
location_id?: string;
|
|
862
|
+
media_name: string;
|
|
863
|
+
}): Promise<Record<string, any>>;
|
|
823
864
|
listActivity(params?: {
|
|
824
865
|
workspace_id?: string;
|
|
825
866
|
post_id?: number;
|
package/dist/lib/api-client.js
CHANGED
|
@@ -333,6 +333,23 @@ export class PosterlyClient {
|
|
|
333
333
|
async deleteGoogleBusinessReviewReply(data) {
|
|
334
334
|
return this.request('DELETE', '/google-business/reviews/reply', data);
|
|
335
335
|
}
|
|
336
|
+
async listGoogleBusinessMedia(params) {
|
|
337
|
+
const searchParams = new URLSearchParams();
|
|
338
|
+
if (params?.workspace_id)
|
|
339
|
+
searchParams.set('workspace_id', params.workspace_id);
|
|
340
|
+
if (params?.account_id)
|
|
341
|
+
searchParams.set('account_id', String(params.account_id));
|
|
342
|
+
if (params?.location_id)
|
|
343
|
+
searchParams.set('location_id', params.location_id);
|
|
344
|
+
const qs = searchParams.toString();
|
|
345
|
+
return this.request('GET', `/google-business/media${qs ? `?${qs}` : ''}`);
|
|
346
|
+
}
|
|
347
|
+
async addGoogleBusinessMedia(data) {
|
|
348
|
+
return this.request('POST', '/google-business/media', data);
|
|
349
|
+
}
|
|
350
|
+
async deleteGoogleBusinessMedia(data) {
|
|
351
|
+
return this.request('DELETE', '/google-business/media', data);
|
|
352
|
+
}
|
|
336
353
|
async listActivity(params) {
|
|
337
354
|
const searchParams = new URLSearchParams();
|
|
338
355
|
if (params?.workspace_id)
|
package/dist/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const POSTERLY_MCP_VERSION = "0.
|
|
1
|
+
export declare const POSTERLY_MCP_VERSION = "0.23.0";
|
package/dist/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const POSTERLY_MCP_VERSION = '0.
|
|
1
|
+
export const POSTERLY_MCP_VERSION = '0.23.0';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
declare const CATEGORIES: readonly ["COVER", "PROFILE", "LOGO", "EXTERIOR", "INTERIOR", "PRODUCT", "AT_WORK", "FOOD_AND_DRINK", "MENU", "COMMON_AREA", "ROOMS", "TEAMS", "ADDITIONAL"];
|
|
4
|
+
export declare const addGoogleBusinessMediaTool: {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
inputSchema: z.ZodObject<{
|
|
8
|
+
workspace_id: z.ZodOptional<z.ZodString>;
|
|
9
|
+
account_id: z.ZodOptional<z.ZodNumber>;
|
|
10
|
+
location_id: z.ZodOptional<z.ZodString>;
|
|
11
|
+
source_url: z.ZodString;
|
|
12
|
+
category: z.ZodEnum<["COVER", "PROFILE", "LOGO", "EXTERIOR", "INTERIOR", "PRODUCT", "AT_WORK", "FOOD_AND_DRINK", "MENU", "COMMON_AREA", "ROOMS", "TEAMS", "ADDITIONAL"]>;
|
|
13
|
+
media_format: z.ZodOptional<z.ZodEnum<["PHOTO", "VIDEO"]>>;
|
|
14
|
+
confirm: z.ZodLiteral<true>;
|
|
15
|
+
}, "strip", z.ZodTypeAny, {
|
|
16
|
+
confirm: true;
|
|
17
|
+
source_url: string;
|
|
18
|
+
category: "COVER" | "PROFILE" | "LOGO" | "EXTERIOR" | "INTERIOR" | "PRODUCT" | "AT_WORK" | "FOOD_AND_DRINK" | "MENU" | "COMMON_AREA" | "ROOMS" | "TEAMS" | "ADDITIONAL";
|
|
19
|
+
workspace_id?: string | undefined;
|
|
20
|
+
account_id?: number | undefined;
|
|
21
|
+
location_id?: string | undefined;
|
|
22
|
+
media_format?: "PHOTO" | "VIDEO" | undefined;
|
|
23
|
+
}, {
|
|
24
|
+
confirm: true;
|
|
25
|
+
source_url: string;
|
|
26
|
+
category: "COVER" | "PROFILE" | "LOGO" | "EXTERIOR" | "INTERIOR" | "PRODUCT" | "AT_WORK" | "FOOD_AND_DRINK" | "MENU" | "COMMON_AREA" | "ROOMS" | "TEAMS" | "ADDITIONAL";
|
|
27
|
+
workspace_id?: string | undefined;
|
|
28
|
+
account_id?: number | undefined;
|
|
29
|
+
location_id?: string | undefined;
|
|
30
|
+
media_format?: "PHOTO" | "VIDEO" | undefined;
|
|
31
|
+
}>;
|
|
32
|
+
execute(client: PosterlyClient, input: {
|
|
33
|
+
workspace_id?: string;
|
|
34
|
+
account_id?: number;
|
|
35
|
+
location_id?: string;
|
|
36
|
+
source_url: string;
|
|
37
|
+
category: (typeof CATEGORIES)[number];
|
|
38
|
+
media_format?: "PHOTO" | "VIDEO";
|
|
39
|
+
confirm: true;
|
|
40
|
+
}): Promise<any>;
|
|
41
|
+
};
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const CATEGORIES = [
|
|
3
|
+
'COVER',
|
|
4
|
+
'PROFILE',
|
|
5
|
+
'LOGO',
|
|
6
|
+
'EXTERIOR',
|
|
7
|
+
'INTERIOR',
|
|
8
|
+
'PRODUCT',
|
|
9
|
+
'AT_WORK',
|
|
10
|
+
'FOOD_AND_DRINK',
|
|
11
|
+
'MENU',
|
|
12
|
+
'COMMON_AREA',
|
|
13
|
+
'ROOMS',
|
|
14
|
+
'TEAMS',
|
|
15
|
+
'ADDITIONAL',
|
|
16
|
+
];
|
|
17
|
+
export const addGoogleBusinessMediaTool = {
|
|
18
|
+
name: 'add_google_business_media',
|
|
19
|
+
description: 'Add a photo or video to a Google Business Profile gallery (the media shown on Maps and Search). Provide a public https source_url first by uploading via upload_media_from_url or upload_media. COVER and PROFILE are single-slot and replace the existing one. WRITE: show the location, category, and media URL to the user, then pass confirm=true only after explicit confirmation.',
|
|
20
|
+
inputSchema: z.object({
|
|
21
|
+
workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
|
|
22
|
+
account_id: z.number().int().positive().optional().describe('Google Business account ID from list_accounts.'),
|
|
23
|
+
location_id: z.string().optional().describe('Google Business location/platform_user_id. Provide account_id or location_id.'),
|
|
24
|
+
source_url: z.string().url().describe('Public https URL of the photo or video (from upload_media / upload_media_from_url).'),
|
|
25
|
+
category: z.enum(CATEGORIES).describe('Which gallery category to add the media to.'),
|
|
26
|
+
media_format: z.enum(['PHOTO', 'VIDEO']).optional().describe('Defaults to PHOTO. Use VIDEO for videos (Google caps profile videos at 30s / 75MB).'),
|
|
27
|
+
confirm: z.literal(true).describe('Must be true after explicit user confirmation.'),
|
|
28
|
+
}),
|
|
29
|
+
async execute(client, input) {
|
|
30
|
+
const { confirm: _confirm, ...payload } = input;
|
|
31
|
+
const result = await client.addGoogleBusinessMedia(payload);
|
|
32
|
+
return result.message || 'Media added to the Google Business profile.';
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
export declare const deleteGoogleBusinessMediaTool: {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: z.ZodObject<{
|
|
7
|
+
workspace_id: z.ZodOptional<z.ZodString>;
|
|
8
|
+
account_id: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
location_id: z.ZodOptional<z.ZodString>;
|
|
10
|
+
media_name: z.ZodString;
|
|
11
|
+
confirm: z.ZodLiteral<true>;
|
|
12
|
+
}, "strip", z.ZodTypeAny, {
|
|
13
|
+
confirm: true;
|
|
14
|
+
media_name: string;
|
|
15
|
+
workspace_id?: string | undefined;
|
|
16
|
+
account_id?: number | undefined;
|
|
17
|
+
location_id?: string | undefined;
|
|
18
|
+
}, {
|
|
19
|
+
confirm: true;
|
|
20
|
+
media_name: string;
|
|
21
|
+
workspace_id?: string | undefined;
|
|
22
|
+
account_id?: number | undefined;
|
|
23
|
+
location_id?: string | undefined;
|
|
24
|
+
}>;
|
|
25
|
+
execute(client: PosterlyClient, input: {
|
|
26
|
+
workspace_id?: string;
|
|
27
|
+
account_id?: number;
|
|
28
|
+
location_id?: string;
|
|
29
|
+
media_name: string;
|
|
30
|
+
confirm: true;
|
|
31
|
+
}): Promise<any>;
|
|
32
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const deleteGoogleBusinessMediaTool = {
|
|
3
|
+
name: 'delete_google_business_media',
|
|
4
|
+
description: 'Remove a photo or video from a Google Business Profile gallery. Get media_name from list_google_business_media. This permanently deletes the item from the public profile. WRITE: show the exact media item and location to the user, then pass confirm=true only after explicit confirmation.',
|
|
5
|
+
inputSchema: z.object({
|
|
6
|
+
workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
|
|
7
|
+
account_id: z.number().int().positive().optional().describe('Google Business account ID from list_accounts.'),
|
|
8
|
+
location_id: z.string().optional().describe('Google Business location/platform_user_id. Provide account_id or location_id.'),
|
|
9
|
+
media_name: z.string().min(1).describe('Full media resource name from list_google_business_media (accounts/.../locations/.../media/...).'),
|
|
10
|
+
confirm: z.literal(true).describe('Must be true after explicit user confirmation.'),
|
|
11
|
+
}),
|
|
12
|
+
async execute(client, input) {
|
|
13
|
+
const { confirm: _confirm, ...payload } = input;
|
|
14
|
+
const result = await client.deleteGoogleBusinessMedia(payload);
|
|
15
|
+
return result.message || 'Media removed from the Google Business profile.';
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
export declare const listGoogleBusinessMediaTool: {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: z.ZodObject<{
|
|
7
|
+
workspace_id: z.ZodOptional<z.ZodString>;
|
|
8
|
+
account_id: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
location_id: z.ZodOptional<z.ZodString>;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
workspace_id?: string | undefined;
|
|
12
|
+
account_id?: number | undefined;
|
|
13
|
+
location_id?: string | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
workspace_id?: string | undefined;
|
|
16
|
+
account_id?: number | undefined;
|
|
17
|
+
location_id?: string | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
execute(client: PosterlyClient, input: {
|
|
20
|
+
workspace_id?: string;
|
|
21
|
+
account_id?: number;
|
|
22
|
+
location_id?: string;
|
|
23
|
+
}): Promise<string>;
|
|
24
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const listGoogleBusinessMediaTool = {
|
|
3
|
+
name: 'list_google_business_media',
|
|
4
|
+
description: 'List the photos and videos on a Google Business Profile gallery (the media shown on Maps and Search) for one location/account or every accessible GBP location. This is separate from media attached to a post. Returns each item\'s resource name, category, format, and URLs.',
|
|
5
|
+
inputSchema: z.object({
|
|
6
|
+
workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
|
|
7
|
+
account_id: z.number().int().positive().optional().describe('Google Business account ID from list_accounts.'),
|
|
8
|
+
location_id: z.string().optional().describe('Google Business location/platform_user_id.'),
|
|
9
|
+
}),
|
|
10
|
+
async execute(client, input) {
|
|
11
|
+
const result = await client.listGoogleBusinessMedia(input);
|
|
12
|
+
if (result.media.length === 0) {
|
|
13
|
+
return 'No photos or videos found on the Google Business profile for your filters.';
|
|
14
|
+
}
|
|
15
|
+
const lines = [`Google Business profile media (${result.media.length}):`];
|
|
16
|
+
for (const item of result.media.slice(0, 50)) {
|
|
17
|
+
const kind = item.mediaFormat === 'VIDEO' ? 'video' : 'photo';
|
|
18
|
+
const category = item.category || 'uncategorised';
|
|
19
|
+
const attribution = item.attribution?.profileName ? ` by ${item.attribution.profileName}` : '';
|
|
20
|
+
lines.push(`- [${kind}] ${category}${attribution} :: ${item.name}`);
|
|
21
|
+
}
|
|
22
|
+
lines.push('', 'Use the resource name (after ::) as media_name to delete an item.');
|
|
23
|
+
return lines.join('\n');
|
|
24
|
+
},
|
|
25
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "posterly-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "MCP server for posterly: schedule and publish social media posts across 11 platforms from any MCP client (Claude, ChatGPT, Cursor, Windsurf, Cline, and more)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://www.poster.ly/mcp",
|
package/src/index.ts
CHANGED
|
@@ -42,6 +42,9 @@ import { auditGoogleBusinessProfileTool } from './tools/audit-google-business-pr
|
|
|
42
42
|
import { suggestGoogleBusinessReviewReplyTool } from './tools/suggest-google-business-review-reply.js';
|
|
43
43
|
import { replyGoogleBusinessReviewTool } from './tools/reply-google-business-review.js';
|
|
44
44
|
import { deleteGoogleBusinessReviewReplyTool } from './tools/delete-google-business-review-reply.js';
|
|
45
|
+
import { listGoogleBusinessMediaTool } from './tools/list-google-business-media.js';
|
|
46
|
+
import { addGoogleBusinessMediaTool } from './tools/add-google-business-media.js';
|
|
47
|
+
import { deleteGoogleBusinessMediaTool } from './tools/delete-google-business-media.js';
|
|
45
48
|
import { listActivityTool } from './tools/list-activity.js';
|
|
46
49
|
import { listWebhooksTool } from './tools/list-webhooks.js';
|
|
47
50
|
import { createWebhookTool } from './tools/create-webhook.js';
|
|
@@ -800,6 +803,48 @@ server.tool(
|
|
|
800
803
|
}
|
|
801
804
|
);
|
|
802
805
|
|
|
806
|
+
server.tool(
|
|
807
|
+
listGoogleBusinessMediaTool.name,
|
|
808
|
+
listGoogleBusinessMediaTool.description,
|
|
809
|
+
listGoogleBusinessMediaTool.inputSchema.shape,
|
|
810
|
+
async (input) => {
|
|
811
|
+
try {
|
|
812
|
+
const text = await listGoogleBusinessMediaTool.execute(client, input as any);
|
|
813
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
814
|
+
} catch (err: any) {
|
|
815
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
server.tool(
|
|
821
|
+
addGoogleBusinessMediaTool.name,
|
|
822
|
+
addGoogleBusinessMediaTool.description,
|
|
823
|
+
addGoogleBusinessMediaTool.inputSchema.shape,
|
|
824
|
+
async (input) => {
|
|
825
|
+
try {
|
|
826
|
+
const text = await addGoogleBusinessMediaTool.execute(client, input as any);
|
|
827
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
828
|
+
} catch (err: any) {
|
|
829
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
server.tool(
|
|
835
|
+
deleteGoogleBusinessMediaTool.name,
|
|
836
|
+
deleteGoogleBusinessMediaTool.description,
|
|
837
|
+
deleteGoogleBusinessMediaTool.inputSchema.shape,
|
|
838
|
+
async (input) => {
|
|
839
|
+
try {
|
|
840
|
+
const text = await deleteGoogleBusinessMediaTool.execute(client, input as any);
|
|
841
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
842
|
+
} catch (err: any) {
|
|
843
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
);
|
|
847
|
+
|
|
803
848
|
server.tool(
|
|
804
849
|
listActivityTool.name,
|
|
805
850
|
listActivityTool.description,
|
package/src/lib/api-client.ts
CHANGED
|
@@ -369,6 +369,26 @@ export interface GoogleBusinessReviewsResponse {
|
|
|
369
369
|
locations: Array<Record<string, unknown>>;
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
+
export interface GoogleBusinessMediaItem {
|
|
373
|
+
name: string;
|
|
374
|
+
mediaFormat: 'PHOTO' | 'VIDEO' | 'MEDIA_FORMAT_UNSPECIFIED';
|
|
375
|
+
category: string | null;
|
|
376
|
+
googleUrl: string | null;
|
|
377
|
+
thumbnailUrl: string | null;
|
|
378
|
+
createTime: string | null;
|
|
379
|
+
widthPixels: number | null;
|
|
380
|
+
heightPixels: number | null;
|
|
381
|
+
attribution: { profileName?: string; profilePhotoUrl?: string; profileUrl?: string } | null;
|
|
382
|
+
account_id?: number;
|
|
383
|
+
location_id?: string | null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export interface GoogleBusinessMediaResponse {
|
|
387
|
+
media: GoogleBusinessMediaItem[];
|
|
388
|
+
total: number;
|
|
389
|
+
locations: Array<Record<string, unknown>>;
|
|
390
|
+
}
|
|
391
|
+
|
|
372
392
|
export interface GoogleBusinessReviewLinkResponse {
|
|
373
393
|
reviewLink: string;
|
|
374
394
|
placeId: string;
|
|
@@ -1117,6 +1137,39 @@ export class PosterlyClient {
|
|
|
1117
1137
|
return this.request('DELETE', '/google-business/reviews/reply', data);
|
|
1118
1138
|
}
|
|
1119
1139
|
|
|
1140
|
+
async listGoogleBusinessMedia(params?: {
|
|
1141
|
+
workspace_id?: string;
|
|
1142
|
+
account_id?: number;
|
|
1143
|
+
location_id?: string;
|
|
1144
|
+
}): Promise<GoogleBusinessMediaResponse> {
|
|
1145
|
+
const searchParams = new URLSearchParams();
|
|
1146
|
+
if (params?.workspace_id) searchParams.set('workspace_id', params.workspace_id);
|
|
1147
|
+
if (params?.account_id) searchParams.set('account_id', String(params.account_id));
|
|
1148
|
+
if (params?.location_id) searchParams.set('location_id', params.location_id);
|
|
1149
|
+
const qs = searchParams.toString();
|
|
1150
|
+
return this.request('GET', `/google-business/media${qs ? `?${qs}` : ''}`);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
async addGoogleBusinessMedia(data: {
|
|
1154
|
+
workspace_id?: string;
|
|
1155
|
+
account_id?: number;
|
|
1156
|
+
location_id?: string;
|
|
1157
|
+
source_url: string;
|
|
1158
|
+
category: string;
|
|
1159
|
+
media_format?: 'PHOTO' | 'VIDEO';
|
|
1160
|
+
}): Promise<Record<string, any>> {
|
|
1161
|
+
return this.request('POST', '/google-business/media', data);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
async deleteGoogleBusinessMedia(data: {
|
|
1165
|
+
workspace_id?: string;
|
|
1166
|
+
account_id?: number;
|
|
1167
|
+
location_id?: string;
|
|
1168
|
+
media_name: string;
|
|
1169
|
+
}): Promise<Record<string, any>> {
|
|
1170
|
+
return this.request('DELETE', '/google-business/media', data);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1120
1173
|
async listActivity(params?: {
|
|
1121
1174
|
workspace_id?: string;
|
|
1122
1175
|
post_id?: number;
|
package/src/lib/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const POSTERLY_MCP_VERSION = '0.
|
|
1
|
+
export const POSTERLY_MCP_VERSION = '0.23.0';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
|
|
4
|
+
const CATEGORIES = [
|
|
5
|
+
'COVER',
|
|
6
|
+
'PROFILE',
|
|
7
|
+
'LOGO',
|
|
8
|
+
'EXTERIOR',
|
|
9
|
+
'INTERIOR',
|
|
10
|
+
'PRODUCT',
|
|
11
|
+
'AT_WORK',
|
|
12
|
+
'FOOD_AND_DRINK',
|
|
13
|
+
'MENU',
|
|
14
|
+
'COMMON_AREA',
|
|
15
|
+
'ROOMS',
|
|
16
|
+
'TEAMS',
|
|
17
|
+
'ADDITIONAL',
|
|
18
|
+
] as const;
|
|
19
|
+
|
|
20
|
+
export const addGoogleBusinessMediaTool = {
|
|
21
|
+
name: 'add_google_business_media',
|
|
22
|
+
description:
|
|
23
|
+
'Add a photo or video to a Google Business Profile gallery (the media shown on Maps and Search). Provide a public https source_url first by uploading via upload_media_from_url or upload_media. COVER and PROFILE are single-slot and replace the existing one. WRITE: show the location, category, and media URL to the user, then pass confirm=true only after explicit confirmation.',
|
|
24
|
+
inputSchema: z.object({
|
|
25
|
+
workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
|
|
26
|
+
account_id: z.number().int().positive().optional().describe('Google Business account ID from list_accounts.'),
|
|
27
|
+
location_id: z.string().optional().describe('Google Business location/platform_user_id. Provide account_id or location_id.'),
|
|
28
|
+
source_url: z.string().url().describe('Public https URL of the photo or video (from upload_media / upload_media_from_url).'),
|
|
29
|
+
category: z.enum(CATEGORIES).describe('Which gallery category to add the media to.'),
|
|
30
|
+
media_format: z.enum(['PHOTO', 'VIDEO']).optional().describe('Defaults to PHOTO. Use VIDEO for videos (Google caps profile videos at 30s / 75MB).'),
|
|
31
|
+
confirm: z.literal(true).describe('Must be true after explicit user confirmation.'),
|
|
32
|
+
}),
|
|
33
|
+
|
|
34
|
+
async execute(
|
|
35
|
+
client: PosterlyClient,
|
|
36
|
+
input: {
|
|
37
|
+
workspace_id?: string;
|
|
38
|
+
account_id?: number;
|
|
39
|
+
location_id?: string;
|
|
40
|
+
source_url: string;
|
|
41
|
+
category: (typeof CATEGORIES)[number];
|
|
42
|
+
media_format?: 'PHOTO' | 'VIDEO';
|
|
43
|
+
confirm: true;
|
|
44
|
+
},
|
|
45
|
+
) {
|
|
46
|
+
const { confirm: _confirm, ...payload } = input;
|
|
47
|
+
const result = await client.addGoogleBusinessMedia(payload);
|
|
48
|
+
return result.message || 'Media added to the Google Business profile.';
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
|
|
4
|
+
export const deleteGoogleBusinessMediaTool = {
|
|
5
|
+
name: 'delete_google_business_media',
|
|
6
|
+
description:
|
|
7
|
+
'Remove a photo or video from a Google Business Profile gallery. Get media_name from list_google_business_media. This permanently deletes the item from the public profile. WRITE: show the exact media item and location to the user, then pass confirm=true only after explicit confirmation.',
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
|
|
10
|
+
account_id: z.number().int().positive().optional().describe('Google Business account ID from list_accounts.'),
|
|
11
|
+
location_id: z.string().optional().describe('Google Business location/platform_user_id. Provide account_id or location_id.'),
|
|
12
|
+
media_name: z.string().min(1).describe('Full media resource name from list_google_business_media (accounts/.../locations/.../media/...).'),
|
|
13
|
+
confirm: z.literal(true).describe('Must be true after explicit user confirmation.'),
|
|
14
|
+
}),
|
|
15
|
+
|
|
16
|
+
async execute(
|
|
17
|
+
client: PosterlyClient,
|
|
18
|
+
input: {
|
|
19
|
+
workspace_id?: string;
|
|
20
|
+
account_id?: number;
|
|
21
|
+
location_id?: string;
|
|
22
|
+
media_name: string;
|
|
23
|
+
confirm: true;
|
|
24
|
+
},
|
|
25
|
+
) {
|
|
26
|
+
const { confirm: _confirm, ...payload } = input;
|
|
27
|
+
const result = await client.deleteGoogleBusinessMedia(payload);
|
|
28
|
+
return result.message || 'Media removed from the Google Business profile.';
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
|
|
4
|
+
export const listGoogleBusinessMediaTool = {
|
|
5
|
+
name: 'list_google_business_media',
|
|
6
|
+
description:
|
|
7
|
+
'List the photos and videos on a Google Business Profile gallery (the media shown on Maps and Search) for one location/account or every accessible GBP location. This is separate from media attached to a post. Returns each item\'s resource name, category, format, and URLs.',
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
workspace_id: z.string().optional().describe('Filter to a workspace ID from whoami.'),
|
|
10
|
+
account_id: z.number().int().positive().optional().describe('Google Business account ID from list_accounts.'),
|
|
11
|
+
location_id: z.string().optional().describe('Google Business location/platform_user_id.'),
|
|
12
|
+
}),
|
|
13
|
+
|
|
14
|
+
async execute(
|
|
15
|
+
client: PosterlyClient,
|
|
16
|
+
input: { workspace_id?: string; account_id?: number; location_id?: string },
|
|
17
|
+
) {
|
|
18
|
+
const result = await client.listGoogleBusinessMedia(input);
|
|
19
|
+
if (result.media.length === 0) {
|
|
20
|
+
return 'No photos or videos found on the Google Business profile for your filters.';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const lines = [`Google Business profile media (${result.media.length}):`];
|
|
24
|
+
for (const item of result.media.slice(0, 50)) {
|
|
25
|
+
const kind = item.mediaFormat === 'VIDEO' ? 'video' : 'photo';
|
|
26
|
+
const category = item.category || 'uncategorised';
|
|
27
|
+
const attribution = item.attribution?.profileName ? ` by ${item.attribution.profileName}` : '';
|
|
28
|
+
lines.push(`- [${kind}] ${category}${attribution} :: ${item.name}`);
|
|
29
|
+
}
|
|
30
|
+
lines.push('', 'Use the resource name (after ::) as media_name to delete an item.');
|
|
31
|
+
return lines.join('\n');
|
|
32
|
+
},
|
|
33
|
+
};
|