posterly-mcp-server 0.38.0 → 0.39.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 +3 -1
- package/dist/index.js +20 -0
- package/dist/lib/api-client.d.ts +27 -0
- package/dist/lib/api-client.js +15 -0
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/tools/create-media-drop.d.ts +20 -0
- package/dist/tools/create-media-drop.js +32 -0
- package/dist/tools/create-signed-upload.d.ts +2 -2
- package/dist/tools/list-media.d.ts +20 -0
- package/dist/tools/list-media.js +38 -0
- package/dist/tools/upload-media-from-url.d.ts +2 -2
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/index.ts +32 -0
- package/src/lib/api-client.ts +40 -0
- package/src/lib/version.ts +1 -1
- package/src/tools/create-media-drop.ts +39 -0
- package/src/tools/list-media.ts +48 -0
- package/tool-annotations.json +4 -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.
|
|
125
|
+
`posterly-mcp-server@0.39.0` exposes 89 tools.
|
|
126
126
|
|
|
127
127
|
Public setup tools work before `POSTERLY_API_KEY` exists:
|
|
128
128
|
|
|
@@ -176,6 +176,8 @@ Authenticated tools require `POSTERLY_API_KEY`:
|
|
|
176
176
|
- `upload_media`
|
|
177
177
|
- `upload_media_from_url`
|
|
178
178
|
- `create_signed_upload`
|
|
179
|
+
- `create_media_drop` (human drop page at https://www.poster.ly/drop/<token> for ChatGPT/Claude laptop files)
|
|
180
|
+
- `list_media`
|
|
179
181
|
- `find_available_slot`
|
|
180
182
|
- `generate_captions`
|
|
181
183
|
- `generate_image`
|
package/dist/index.js
CHANGED
|
@@ -74,6 +74,8 @@ import { getPlatformSchemaTool } from './tools/get-platform-schema.js';
|
|
|
74
74
|
import { triggerPlatformHelperTool } from './tools/trigger-platform-helper.js';
|
|
75
75
|
import { getXPostingQuotaTool } from './tools/get-x-posting-quota.js';
|
|
76
76
|
import { createSignedUploadTool } from './tools/create-signed-upload.js';
|
|
77
|
+
import { createMediaDropTool } from './tools/create-media-drop.js';
|
|
78
|
+
import { listMediaTool } from './tools/list-media.js';
|
|
77
79
|
import { uploadMediaFromUrlTool } from './tools/upload-media-from-url.js';
|
|
78
80
|
import { getConnectLinkTool } from './tools/get-connect-link.js';
|
|
79
81
|
import { connectAccountTool } from './tools/connect-account.js';
|
|
@@ -502,6 +504,24 @@ server.tool(createSignedUploadTool.name, createSignedUploadTool.description, cre
|
|
|
502
504
|
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
503
505
|
}
|
|
504
506
|
});
|
|
507
|
+
server.tool(createMediaDropTool.name, createMediaDropTool.description, createMediaDropTool.inputSchema.shape, getToolAnnotations(createMediaDropTool.name), async (input) => {
|
|
508
|
+
try {
|
|
509
|
+
const text = await createMediaDropTool.execute(client, input);
|
|
510
|
+
return { content: [{ type: 'text', text }] };
|
|
511
|
+
}
|
|
512
|
+
catch (err) {
|
|
513
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
server.tool(listMediaTool.name, listMediaTool.description, listMediaTool.inputSchema.shape, getToolAnnotations(listMediaTool.name), async (input) => {
|
|
517
|
+
try {
|
|
518
|
+
const text = await listMediaTool.execute(client, input);
|
|
519
|
+
return { content: [{ type: 'text', text }] };
|
|
520
|
+
}
|
|
521
|
+
catch (err) {
|
|
522
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
523
|
+
}
|
|
524
|
+
});
|
|
505
525
|
server.tool(getVideoOptionsTool.name, getVideoOptionsTool.description, getVideoOptionsTool.inputSchema.shape, getToolAnnotations(getVideoOptionsTool.name), async () => {
|
|
506
526
|
try {
|
|
507
527
|
const text = await getVideoOptionsTool.execute(client);
|
package/dist/lib/api-client.d.ts
CHANGED
|
@@ -1262,6 +1262,33 @@ export declare class PosterlyClient {
|
|
|
1262
1262
|
public_url: string;
|
|
1263
1263
|
headers?: Record<string, string>;
|
|
1264
1264
|
}>;
|
|
1265
|
+
createMediaDrop(input?: {
|
|
1266
|
+
filename?: string;
|
|
1267
|
+
maxFiles?: number;
|
|
1268
|
+
}): Promise<{
|
|
1269
|
+
session_id: string;
|
|
1270
|
+
drop_url: string;
|
|
1271
|
+
expires_at: string;
|
|
1272
|
+
max_bytes: number;
|
|
1273
|
+
max_files: number;
|
|
1274
|
+
instructions: string;
|
|
1275
|
+
}>;
|
|
1276
|
+
listMedia(params?: {
|
|
1277
|
+
limit?: number;
|
|
1278
|
+
dropSessionId?: string;
|
|
1279
|
+
}): Promise<{
|
|
1280
|
+
media: Array<{
|
|
1281
|
+
id: string;
|
|
1282
|
+
public_url: string;
|
|
1283
|
+
filename: string;
|
|
1284
|
+
path: string;
|
|
1285
|
+
mime: string | null;
|
|
1286
|
+
bytes: number | null;
|
|
1287
|
+
created_at: string;
|
|
1288
|
+
drop_session_id: string | null;
|
|
1289
|
+
}>;
|
|
1290
|
+
total: number;
|
|
1291
|
+
}>;
|
|
1265
1292
|
uploadMedia(input: {
|
|
1266
1293
|
filePath?: string;
|
|
1267
1294
|
base64Data?: string;
|
package/dist/lib/api-client.js
CHANGED
|
@@ -506,6 +506,21 @@ export class PosterlyClient {
|
|
|
506
506
|
size,
|
|
507
507
|
});
|
|
508
508
|
}
|
|
509
|
+
async createMediaDrop(input) {
|
|
510
|
+
return this.request('POST', '/media/drop-sessions', {
|
|
511
|
+
filename: input?.filename,
|
|
512
|
+
max_files: input?.maxFiles,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
async listMedia(params) {
|
|
516
|
+
const searchParams = new URLSearchParams();
|
|
517
|
+
if (params?.limit)
|
|
518
|
+
searchParams.set('limit', String(params.limit));
|
|
519
|
+
if (params?.dropSessionId)
|
|
520
|
+
searchParams.set('drop_session_id', params.dropSessionId);
|
|
521
|
+
const qs = searchParams.toString();
|
|
522
|
+
return this.request('GET', `/media${qs ? `?${qs}` : ''}`);
|
|
523
|
+
}
|
|
509
524
|
async uploadMedia(input) {
|
|
510
525
|
this.requireApiKey();
|
|
511
526
|
let fileBuffer;
|
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.39.0";
|
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.
|
|
8
|
+
export const POSTERLY_MCP_VERSION = '0.39.0';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
export declare const createMediaDropTool: {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: z.ZodObject<{
|
|
7
|
+
filename: z.ZodOptional<z.ZodString>;
|
|
8
|
+
max_files: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
filename?: string | undefined;
|
|
11
|
+
max_files?: number | undefined;
|
|
12
|
+
}, {
|
|
13
|
+
filename?: string | undefined;
|
|
14
|
+
max_files?: number | undefined;
|
|
15
|
+
}>;
|
|
16
|
+
execute(client: PosterlyClient, input: {
|
|
17
|
+
filename?: string;
|
|
18
|
+
max_files?: number;
|
|
19
|
+
}): Promise<string>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const createMediaDropTool = {
|
|
3
|
+
name: 'create_media_drop',
|
|
4
|
+
description: 'Create a human drop page so ChatGPT or Claude users can upload a laptop file without dashboard login. Returns drop_url (https://www.poster.ly/drop/<token>). Send that URL to the user, wait, then call list_media. This is not the chat paperclip and not create_signed_upload.',
|
|
5
|
+
inputSchema: z.object({
|
|
6
|
+
filename: z.string().optional().describe('Optional filename hint for the user, e.g. launch-video.mp4'),
|
|
7
|
+
max_files: z
|
|
8
|
+
.number()
|
|
9
|
+
.int()
|
|
10
|
+
.min(1)
|
|
11
|
+
.max(10)
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('How many files the drop can accept. Default 10, max 10.'),
|
|
14
|
+
}),
|
|
15
|
+
async execute(client, input) {
|
|
16
|
+
const drop = await client.createMediaDrop({
|
|
17
|
+
filename: input.filename,
|
|
18
|
+
maxFiles: input.max_files,
|
|
19
|
+
});
|
|
20
|
+
return [
|
|
21
|
+
'Media drop created. Send this URL to the user. They open it with no posterly login and drop the file.',
|
|
22
|
+
`• Drop URL: ${drop.drop_url}`,
|
|
23
|
+
`• Session ID: ${drop.session_id}`,
|
|
24
|
+
`• Expires: ${drop.expires_at}`,
|
|
25
|
+
`• Max bytes: ${drop.max_bytes}`,
|
|
26
|
+
`• Max files: ${drop.max_files}`,
|
|
27
|
+
drop.instructions,
|
|
28
|
+
'After they finish, call list_media with this drop_session_id, then validate_post / create_post using public_url.',
|
|
29
|
+
'Chat attachments never reach MCP. HEIC and PDF are rejected.',
|
|
30
|
+
].join('\n');
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -8,12 +8,12 @@ export declare const createSignedUploadTool: {
|
|
|
8
8
|
content_type: z.ZodString;
|
|
9
9
|
size: z.ZodNumber;
|
|
10
10
|
}, "strip", z.ZodTypeAny, {
|
|
11
|
-
content_type: string;
|
|
12
11
|
filename: string;
|
|
12
|
+
content_type: string;
|
|
13
13
|
size: number;
|
|
14
14
|
}, {
|
|
15
|
-
content_type: string;
|
|
16
15
|
filename: string;
|
|
16
|
+
content_type: string;
|
|
17
17
|
size: number;
|
|
18
18
|
}>;
|
|
19
19
|
execute(client: PosterlyClient, input: {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
export declare const listMediaTool: {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: z.ZodObject<{
|
|
7
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
drop_session_id: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
limit?: number | undefined;
|
|
11
|
+
drop_session_id?: string | undefined;
|
|
12
|
+
}, {
|
|
13
|
+
limit?: number | undefined;
|
|
14
|
+
drop_session_id?: string | undefined;
|
|
15
|
+
}>;
|
|
16
|
+
execute(client: PosterlyClient, input: {
|
|
17
|
+
limit?: number;
|
|
18
|
+
drop_session_id?: string;
|
|
19
|
+
}): Promise<string>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const listMediaTool = {
|
|
3
|
+
name: 'list_media',
|
|
4
|
+
description: 'List the newest media assets for the authenticated user. After create_media_drop, pass drop_session_id to see files the human uploaded on /drop/<token>. Use public_url with validate_post / create_post.',
|
|
5
|
+
inputSchema: z.object({
|
|
6
|
+
limit: z
|
|
7
|
+
.number()
|
|
8
|
+
.int()
|
|
9
|
+
.min(1)
|
|
10
|
+
.max(50)
|
|
11
|
+
.optional()
|
|
12
|
+
.describe('Number of assets to return (default 10, max 50)'),
|
|
13
|
+
drop_session_id: z
|
|
14
|
+
.string()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('Filter to files uploaded through one create_media_drop session'),
|
|
17
|
+
}),
|
|
18
|
+
async execute(client, input) {
|
|
19
|
+
const result = await client.listMedia({
|
|
20
|
+
limit: input.limit,
|
|
21
|
+
dropSessionId: input.drop_session_id,
|
|
22
|
+
});
|
|
23
|
+
if (result.media.length === 0) {
|
|
24
|
+
return 'No media found. If you just sent a drop URL, wait for the user to finish uploading, then call list_media again.';
|
|
25
|
+
}
|
|
26
|
+
const lines = result.media.map((item) => {
|
|
27
|
+
const size = typeof item.bytes === 'number' ? `${item.bytes} bytes` : 'unknown size';
|
|
28
|
+
const drop = item.drop_session_id ? ` drop=${item.drop_session_id}` : '';
|
|
29
|
+
return `• ${item.filename} (${item.mime || 'unknown'}, ${size})${drop}\n public_url: ${item.public_url}`;
|
|
30
|
+
});
|
|
31
|
+
return [
|
|
32
|
+
`Media (${result.media.length} of ${result.total}):`,
|
|
33
|
+
...lines,
|
|
34
|
+
'',
|
|
35
|
+
'Pass public_url to validate_post / create_post. Do not invent URLs.',
|
|
36
|
+
].join('\n');
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -9,12 +9,12 @@ export declare const uploadMediaFromUrlTool: {
|
|
|
9
9
|
content_type: z.ZodOptional<z.ZodString>;
|
|
10
10
|
}, "strip", z.ZodTypeAny, {
|
|
11
11
|
url: string;
|
|
12
|
-
content_type?: string | undefined;
|
|
13
12
|
filename?: string | undefined;
|
|
13
|
+
content_type?: string | undefined;
|
|
14
14
|
}, {
|
|
15
15
|
url: string;
|
|
16
|
-
content_type?: string | undefined;
|
|
17
16
|
filename?: string | undefined;
|
|
17
|
+
content_type?: string | undefined;
|
|
18
18
|
}>;
|
|
19
19
|
execute(client: PosterlyClient, input: {
|
|
20
20
|
url: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "posterly-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
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.
|
|
6
|
+
"version": "0.39.0",
|
|
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.
|
|
17
|
+
"version": "0.39.0",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
|
20
20
|
},
|
package/src/index.ts
CHANGED
|
@@ -75,6 +75,8 @@ import { getPlatformSchemaTool } from './tools/get-platform-schema.js';
|
|
|
75
75
|
import { triggerPlatformHelperTool } from './tools/trigger-platform-helper.js';
|
|
76
76
|
import { getXPostingQuotaTool } from './tools/get-x-posting-quota.js';
|
|
77
77
|
import { createSignedUploadTool } from './tools/create-signed-upload.js';
|
|
78
|
+
import { createMediaDropTool } from './tools/create-media-drop.js';
|
|
79
|
+
import { listMediaTool } from './tools/list-media.js';
|
|
78
80
|
import { uploadMediaFromUrlTool } from './tools/upload-media-from-url.js';
|
|
79
81
|
import { getConnectLinkTool } from './tools/get-connect-link.js';
|
|
80
82
|
import { connectAccountTool } from './tools/connect-account.js';
|
|
@@ -776,6 +778,36 @@ server.tool(
|
|
|
776
778
|
}
|
|
777
779
|
);
|
|
778
780
|
|
|
781
|
+
server.tool(
|
|
782
|
+
createMediaDropTool.name,
|
|
783
|
+
createMediaDropTool.description,
|
|
784
|
+
createMediaDropTool.inputSchema.shape,
|
|
785
|
+
getToolAnnotations(createMediaDropTool.name),
|
|
786
|
+
async (input) => {
|
|
787
|
+
try {
|
|
788
|
+
const text = await createMediaDropTool.execute(client, input as any);
|
|
789
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
790
|
+
} catch (err: any) {
|
|
791
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
server.tool(
|
|
797
|
+
listMediaTool.name,
|
|
798
|
+
listMediaTool.description,
|
|
799
|
+
listMediaTool.inputSchema.shape,
|
|
800
|
+
getToolAnnotations(listMediaTool.name),
|
|
801
|
+
async (input) => {
|
|
802
|
+
try {
|
|
803
|
+
const text = await listMediaTool.execute(client, input as any);
|
|
804
|
+
return { content: [{ type: 'text' as const, text }] };
|
|
805
|
+
} catch (err: any) {
|
|
806
|
+
return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
);
|
|
810
|
+
|
|
779
811
|
server.tool(
|
|
780
812
|
getVideoOptionsTool.name,
|
|
781
813
|
getVideoOptionsTool.description,
|
package/src/lib/api-client.ts
CHANGED
|
@@ -1702,6 +1702,46 @@ export class PosterlyClient {
|
|
|
1702
1702
|
});
|
|
1703
1703
|
}
|
|
1704
1704
|
|
|
1705
|
+
async createMediaDrop(input?: {
|
|
1706
|
+
filename?: string;
|
|
1707
|
+
maxFiles?: number;
|
|
1708
|
+
}): Promise<{
|
|
1709
|
+
session_id: string;
|
|
1710
|
+
drop_url: string;
|
|
1711
|
+
expires_at: string;
|
|
1712
|
+
max_bytes: number;
|
|
1713
|
+
max_files: number;
|
|
1714
|
+
instructions: string;
|
|
1715
|
+
}> {
|
|
1716
|
+
return this.request('POST', '/media/drop-sessions', {
|
|
1717
|
+
filename: input?.filename,
|
|
1718
|
+
max_files: input?.maxFiles,
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
async listMedia(params?: {
|
|
1723
|
+
limit?: number;
|
|
1724
|
+
dropSessionId?: string;
|
|
1725
|
+
}): Promise<{
|
|
1726
|
+
media: Array<{
|
|
1727
|
+
id: string;
|
|
1728
|
+
public_url: string;
|
|
1729
|
+
filename: string;
|
|
1730
|
+
path: string;
|
|
1731
|
+
mime: string | null;
|
|
1732
|
+
bytes: number | null;
|
|
1733
|
+
created_at: string;
|
|
1734
|
+
drop_session_id: string | null;
|
|
1735
|
+
}>;
|
|
1736
|
+
total: number;
|
|
1737
|
+
}> {
|
|
1738
|
+
const searchParams = new URLSearchParams();
|
|
1739
|
+
if (params?.limit) searchParams.set('limit', String(params.limit));
|
|
1740
|
+
if (params?.dropSessionId) searchParams.set('drop_session_id', params.dropSessionId);
|
|
1741
|
+
const qs = searchParams.toString();
|
|
1742
|
+
return this.request('GET', `/media${qs ? `?${qs}` : ''}`);
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1705
1745
|
async uploadMedia(input: {
|
|
1706
1746
|
filePath?: string;
|
|
1707
1747
|
base64Data?: string;
|
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.
|
|
8
|
+
export const POSTERLY_MCP_VERSION = '0.39.0';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
|
|
4
|
+
export const createMediaDropTool = {
|
|
5
|
+
name: 'create_media_drop',
|
|
6
|
+
description:
|
|
7
|
+
'Create a human drop page so ChatGPT or Claude users can upload a laptop file without dashboard login. Returns drop_url (https://www.poster.ly/drop/<token>). Send that URL to the user, wait, then call list_media. This is not the chat paperclip and not create_signed_upload.',
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
filename: z.string().optional().describe('Optional filename hint for the user, e.g. launch-video.mp4'),
|
|
10
|
+
max_files: z
|
|
11
|
+
.number()
|
|
12
|
+
.int()
|
|
13
|
+
.min(1)
|
|
14
|
+
.max(10)
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('How many files the drop can accept. Default 10, max 10.'),
|
|
17
|
+
}),
|
|
18
|
+
|
|
19
|
+
async execute(
|
|
20
|
+
client: PosterlyClient,
|
|
21
|
+
input: { filename?: string; max_files?: number },
|
|
22
|
+
) {
|
|
23
|
+
const drop = await client.createMediaDrop({
|
|
24
|
+
filename: input.filename,
|
|
25
|
+
maxFiles: input.max_files,
|
|
26
|
+
});
|
|
27
|
+
return [
|
|
28
|
+
'Media drop created. Send this URL to the user. They open it with no posterly login and drop the file.',
|
|
29
|
+
`• Drop URL: ${drop.drop_url}`,
|
|
30
|
+
`• Session ID: ${drop.session_id}`,
|
|
31
|
+
`• Expires: ${drop.expires_at}`,
|
|
32
|
+
`• Max bytes: ${drop.max_bytes}`,
|
|
33
|
+
`• Max files: ${drop.max_files}`,
|
|
34
|
+
drop.instructions,
|
|
35
|
+
'After they finish, call list_media with this drop_session_id, then validate_post / create_post using public_url.',
|
|
36
|
+
'Chat attachments never reach MCP. HEIC and PDF are rejected.',
|
|
37
|
+
].join('\n');
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PosterlyClient } from '../lib/api-client.js';
|
|
3
|
+
|
|
4
|
+
export const listMediaTool = {
|
|
5
|
+
name: 'list_media',
|
|
6
|
+
description:
|
|
7
|
+
'List the newest media assets for the authenticated user. After create_media_drop, pass drop_session_id to see files the human uploaded on /drop/<token>. Use public_url with validate_post / create_post.',
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
limit: z
|
|
10
|
+
.number()
|
|
11
|
+
.int()
|
|
12
|
+
.min(1)
|
|
13
|
+
.max(50)
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Number of assets to return (default 10, max 50)'),
|
|
16
|
+
drop_session_id: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe('Filter to files uploaded through one create_media_drop session'),
|
|
20
|
+
}),
|
|
21
|
+
|
|
22
|
+
async execute(
|
|
23
|
+
client: PosterlyClient,
|
|
24
|
+
input: { limit?: number; drop_session_id?: string },
|
|
25
|
+
) {
|
|
26
|
+
const result = await client.listMedia({
|
|
27
|
+
limit: input.limit,
|
|
28
|
+
dropSessionId: input.drop_session_id,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (result.media.length === 0) {
|
|
32
|
+
return 'No media found. If you just sent a drop URL, wait for the user to finish uploading, then call list_media again.';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const lines = result.media.map((item) => {
|
|
36
|
+
const size = typeof item.bytes === 'number' ? `${item.bytes} bytes` : 'unknown size';
|
|
37
|
+
const drop = item.drop_session_id ? ` drop=${item.drop_session_id}` : '';
|
|
38
|
+
return `• ${item.filename} (${item.mime || 'unknown'}, ${size})${drop}\n public_url: ${item.public_url}`;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return [
|
|
42
|
+
`Media (${result.media.length} of ${result.total}):`,
|
|
43
|
+
...lines,
|
|
44
|
+
'',
|
|
45
|
+
'Pass public_url to validate_post / create_post. Do not invent URLs.',
|
|
46
|
+
].join('\n');
|
|
47
|
+
},
|
|
48
|
+
};
|
package/tool-annotations.json
CHANGED
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
"upload_media",
|
|
46
46
|
"upload_media_from_url",
|
|
47
47
|
"create_signed_upload",
|
|
48
|
+
"create_media_drop",
|
|
49
|
+
"list_media",
|
|
48
50
|
"get_video_options",
|
|
49
51
|
"run_video_function",
|
|
50
52
|
"generate_video",
|
|
@@ -113,6 +115,7 @@
|
|
|
113
115
|
"validate_post",
|
|
114
116
|
"find_available_slot",
|
|
115
117
|
"list_posts",
|
|
118
|
+
"list_media",
|
|
116
119
|
"get_video_options",
|
|
117
120
|
"get_video_job",
|
|
118
121
|
"get_post",
|
|
@@ -196,6 +199,7 @@
|
|
|
196
199
|
"create_posts_batch",
|
|
197
200
|
"upload_media_from_url",
|
|
198
201
|
"create_signed_upload",
|
|
202
|
+
"create_media_drop",
|
|
199
203
|
"run_video_function",
|
|
200
204
|
"generate_video",
|
|
201
205
|
"get_video_job",
|