posterly-mcp-server 0.30.0 → 0.32.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 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.30.0` exposes 76 tools.
125
+ `posterly-mcp-server@0.32.0` exposes 77 tools.
126
126
 
127
127
  Public setup tools work before `POSTERLY_API_KEY` exists:
128
128
 
@@ -165,6 +165,7 @@ Authenticated tools require `POSTERLY_API_KEY`:
165
165
  - `create_post` (supports `thread_posts: string[]` for X / Threads reply chains, plus `platform_settings` for platform-specific composer controls)
166
166
  - `validate_post` (checks and normalizes a post without creating it; call before requesting confirmation for `create_post`)
167
167
  - `submit_agent_feedback` (writes bounded private operational telemetry after a real workflow outcome; never include secrets, prompts, captions, media URLs, or personal data)
168
+ - `submit_product_feedback` (files a product bug/idea/feedback on the public dashboard board after explicit user confirmation; not for tool failures)
168
169
  - `create_posts_batch` (create up to 25 confirmed posts in one API request)
169
170
  - `update_post` (also accepts `platform_settings`)
170
171
  - `update_post_status` (pause, resume, schedule, or draft a post after confirmation)
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ import { dismissSuggestionTool } from './tools/dismiss-suggestion.js';
22
22
  import { createPostTool } from './tools/create-post.js';
23
23
  import { validatePostTool } from './tools/validate-post.js';
24
24
  import { submitAgentFeedbackTool } from './tools/submit-agent-feedback.js';
25
+ import { submitProductFeedbackTool } from './tools/submit-product-feedback.js';
25
26
  import { createPostsBatchTool } from './tools/create-posts-batch.js';
26
27
  import { findSlotTool } from './tools/find-slot.js';
27
28
  import { listPostsTool } from './tools/list-posts.js';
@@ -401,6 +402,15 @@ server.tool(submitAgentFeedbackTool.name, submitAgentFeedbackTool.description, s
401
402
  return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
402
403
  }
403
404
  });
405
+ server.tool(submitProductFeedbackTool.name, submitProductFeedbackTool.description, submitProductFeedbackTool.inputSchema.shape, getToolAnnotations(submitProductFeedbackTool.name), async (input) => {
406
+ try {
407
+ const text = await submitProductFeedbackTool.execute(client, input);
408
+ return { content: [{ type: 'text', text }] };
409
+ }
410
+ catch (err) {
411
+ return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
412
+ }
413
+ });
404
414
  server.tool(validatePostTool.name, validatePostTool.description, validatePostTool.inputSchema.shape, getToolAnnotations(validatePostTool.name), async (input) => {
405
415
  try {
406
416
  const text = await validatePostTool.execute(client, input);
@@ -219,6 +219,24 @@ export type AgentFeedbackResponse = {
219
219
  feedback_id: string;
220
220
  request_id: string;
221
221
  };
222
+ export type ProductFeedbackPayload = {
223
+ category: 'bug' | 'idea' | 'feedback';
224
+ title: string;
225
+ description?: string;
226
+ client?: string;
227
+ confirm: true;
228
+ source?: 'mcp' | 'cli' | 'rest' | 'skill';
229
+ context?: {
230
+ request_id?: string;
231
+ related_tool?: string;
232
+ };
233
+ };
234
+ export type ProductFeedbackResponse = {
235
+ accepted: true;
236
+ feedback_id: number;
237
+ board_url: string;
238
+ request_id: string;
239
+ };
222
240
  export type BatchCreatePostsResponse = {
223
241
  posts: Array<CreatePostResponse & {
224
242
  index: number;
@@ -908,6 +926,7 @@ export declare class PosterlyClient {
908
926
  createPost(data: CreatePostPayload): Promise<CreatePostResponse>;
909
927
  validatePost(data: CreatePostPayload): Promise<ValidatePostResponse>;
910
928
  submitAgentFeedback(data: AgentFeedbackPayload): Promise<AgentFeedbackResponse>;
929
+ submitProductFeedback(data: ProductFeedbackPayload): Promise<ProductFeedbackResponse>;
911
930
  createPostsBatch(data: {
912
931
  posts: CreatePostPayload[];
913
932
  }): Promise<BatchCreatePostsResponse>;
@@ -259,6 +259,9 @@ export class PosterlyClient {
259
259
  async submitAgentFeedback(data) {
260
260
  return this.request('POST', '/agent-feedback', data);
261
261
  }
262
+ async submitProductFeedback(data) {
263
+ return this.request('POST', '/feedback', data);
264
+ }
262
265
  async createPostsBatch(data) {
263
266
  return this.request('POST', '/posts/batch', data);
264
267
  }
@@ -1 +1 @@
1
- export declare const POSTERLY_MCP_VERSION = "0.30.0";
1
+ export declare const POSTERLY_MCP_VERSION = "0.32.0";
@@ -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.30.0';
8
+ export const POSTERLY_MCP_VERSION = '0.32.0';
@@ -14,16 +14,16 @@ export declare const addGoogleBusinessMediaTool: {
14
14
  confirm: z.ZodLiteral<true>;
15
15
  }, "strip", z.ZodTypeAny, {
16
16
  confirm: true;
17
- source_url: string;
18
17
  category: "COVER" | "PROFILE" | "LOGO" | "EXTERIOR" | "INTERIOR" | "PRODUCT" | "AT_WORK" | "FOOD_AND_DRINK" | "MENU" | "COMMON_AREA" | "ROOMS" | "TEAMS" | "ADDITIONAL";
18
+ source_url: string;
19
19
  workspace_id?: string | undefined;
20
20
  account_id?: number | undefined;
21
21
  location_id?: string | undefined;
22
22
  media_format?: "PHOTO" | "VIDEO" | undefined;
23
23
  }, {
24
24
  confirm: true;
25
- source_url: string;
26
25
  category: "COVER" | "PROFILE" | "LOGO" | "EXTERIOR" | "INTERIOR" | "PRODUCT" | "AT_WORK" | "FOOD_AND_DRINK" | "MENU" | "COMMON_AREA" | "ROOMS" | "TEAMS" | "ADDITIONAL";
26
+ source_url: string;
27
27
  workspace_id?: string | undefined;
28
28
  account_id?: number | undefined;
29
29
  location_id?: string | undefined;
@@ -0,0 +1,88 @@
1
+ import { z } from 'zod';
2
+ import type { PosterlyClient } from '../lib/api-client.js';
3
+ export declare const submitProductFeedbackInputSchema: z.ZodObject<{
4
+ category: z.ZodEnum<["bug", "idea", "feedback"]>;
5
+ title: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
6
+ description: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>>;
7
+ client: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
8
+ confirm: z.ZodLiteral<true>;
9
+ source: z.ZodOptional<z.ZodEnum<["mcp", "cli", "rest", "skill"]>>;
10
+ context: z.ZodOptional<z.ZodObject<{
11
+ request_id: z.ZodOptional<z.ZodString>;
12
+ related_tool: z.ZodOptional<z.ZodString>;
13
+ }, "strict", z.ZodTypeAny, {
14
+ request_id?: string | undefined;
15
+ related_tool?: string | undefined;
16
+ }, {
17
+ request_id?: string | undefined;
18
+ related_tool?: string | undefined;
19
+ }>>;
20
+ }, "strict", z.ZodTypeAny, {
21
+ title: string;
22
+ confirm: true;
23
+ category: "bug" | "idea" | "feedback";
24
+ description?: string | undefined;
25
+ source?: "mcp" | "cli" | "rest" | "skill" | undefined;
26
+ client?: string | undefined;
27
+ context?: {
28
+ request_id?: string | undefined;
29
+ related_tool?: string | undefined;
30
+ } | undefined;
31
+ }, {
32
+ title: string;
33
+ confirm: true;
34
+ category: "bug" | "idea" | "feedback";
35
+ description?: string | undefined;
36
+ source?: "mcp" | "cli" | "rest" | "skill" | undefined;
37
+ client?: string | undefined;
38
+ context?: {
39
+ request_id?: string | undefined;
40
+ related_tool?: string | undefined;
41
+ } | undefined;
42
+ }>;
43
+ export type SubmitProductFeedbackInput = z.infer<typeof submitProductFeedbackInputSchema>;
44
+ export declare const submitProductFeedbackTool: {
45
+ name: string;
46
+ description: string;
47
+ inputSchema: z.ZodObject<{
48
+ category: z.ZodEnum<["bug", "idea", "feedback"]>;
49
+ title: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>;
50
+ description: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>>;
51
+ client: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
52
+ confirm: z.ZodLiteral<true>;
53
+ source: z.ZodOptional<z.ZodEnum<["mcp", "cli", "rest", "skill"]>>;
54
+ context: z.ZodOptional<z.ZodObject<{
55
+ request_id: z.ZodOptional<z.ZodString>;
56
+ related_tool: z.ZodOptional<z.ZodString>;
57
+ }, "strict", z.ZodTypeAny, {
58
+ request_id?: string | undefined;
59
+ related_tool?: string | undefined;
60
+ }, {
61
+ request_id?: string | undefined;
62
+ related_tool?: string | undefined;
63
+ }>>;
64
+ }, "strict", z.ZodTypeAny, {
65
+ title: string;
66
+ confirm: true;
67
+ category: "bug" | "idea" | "feedback";
68
+ description?: string | undefined;
69
+ source?: "mcp" | "cli" | "rest" | "skill" | undefined;
70
+ client?: string | undefined;
71
+ context?: {
72
+ request_id?: string | undefined;
73
+ related_tool?: string | undefined;
74
+ } | undefined;
75
+ }, {
76
+ title: string;
77
+ confirm: true;
78
+ category: "bug" | "idea" | "feedback";
79
+ description?: string | undefined;
80
+ source?: "mcp" | "cli" | "rest" | "skill" | undefined;
81
+ client?: string | undefined;
82
+ context?: {
83
+ request_id?: string | undefined;
84
+ related_tool?: string | undefined;
85
+ } | undefined;
86
+ }>;
87
+ execute(client: PosterlyClient, input: SubmitProductFeedbackInput): Promise<string>;
88
+ };
@@ -0,0 +1,39 @@
1
+ import { z } from 'zod';
2
+ const tokenSchema = z.string().regex(/^[a-z0-9][a-z0-9_.:-]{0,63}$/);
3
+ const printableSchema = (maxLength) => z.string().trim().min(1).max(maxLength).refine((value) => !/[\u0000-\u001f\u007f]/.test(value), 'Control characters are not allowed');
4
+ const secretFreeSchema = (maxLength) => printableSchema(maxLength).refine((value) => !/(?:pst_(?:live|test)_|sk-[a-z0-9_-]{12,})/i.test(value), 'Must not contain secrets');
5
+ export const submitProductFeedbackInputSchema = z.object({
6
+ category: z.enum(['bug', 'idea', 'feedback']),
7
+ title: secretFreeSchema(120).refine((value) => value.length >= 3, 'Title must be at least 3 characters'),
8
+ description: secretFreeSchema(4000).optional(),
9
+ client: printableSchema(80).optional(),
10
+ confirm: z.literal(true),
11
+ source: z.enum(['mcp', 'cli', 'rest', 'skill']).optional(),
12
+ context: z
13
+ .object({
14
+ request_id: z.string().regex(/^[a-zA-Z0-9_.:-]{8,80}$/).optional(),
15
+ related_tool: tokenSchema.optional(),
16
+ })
17
+ .strict()
18
+ .optional(),
19
+ }).strict();
20
+ export const submitProductFeedbackTool = {
21
+ name: 'submit_product_feedback',
22
+ description: 'File a product bug, idea, or general feedback on the public posterly feedback board (same board users see in the dashboard). Only call this when the human explicitly asks to report a bug, suggest a feature, or send feedback to posterly. Require confirm=true after they approve the title and category. Never use this for tool failures or retries - use submit_agent_feedback for private operational telemetry. Never include API keys, secrets, captions, media URLs, or personal data about third parties.',
23
+ inputSchema: submitProductFeedbackInputSchema,
24
+ async execute(client, input) {
25
+ if (input.confirm !== true) {
26
+ return 'Refusing to file product feedback without confirm=true after explicit user approval of the title and category.';
27
+ }
28
+ const result = await client.submitProductFeedback({
29
+ ...input,
30
+ source: input.source || 'mcp',
31
+ });
32
+ return [
33
+ 'Product feedback filed on the public board.',
34
+ `• Feedback ID: ${result.feedback_id}`,
35
+ `• Board: ${result.board_url}`,
36
+ `• Request ID: ${result.request_id}`,
37
+ ].join('\n');
38
+ },
39
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "posterly-mcp-server",
3
- "version": "0.30.0",
3
+ "version": "0.32.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.30.0",
6
+ "version": "0.32.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.30.0",
17
+ "version": "0.32.0",
18
18
  "transport": {
19
19
  "type": "stdio"
20
20
  },
package/src/index.ts CHANGED
@@ -23,6 +23,7 @@ import { dismissSuggestionTool } from './tools/dismiss-suggestion.js';
23
23
  import { createPostTool } from './tools/create-post.js';
24
24
  import { validatePostTool } from './tools/validate-post.js';
25
25
  import { submitAgentFeedbackTool } from './tools/submit-agent-feedback.js';
26
+ import { submitProductFeedbackTool } from './tools/submit-product-feedback.js';
26
27
  import { createPostsBatchTool } from './tools/create-posts-batch.js';
27
28
  import { findSlotTool } from './tools/find-slot.js';
28
29
  import { listPostsTool } from './tools/list-posts.js';
@@ -615,6 +616,21 @@ server.tool(
615
616
  }
616
617
  );
617
618
 
619
+ server.tool(
620
+ submitProductFeedbackTool.name,
621
+ submitProductFeedbackTool.description,
622
+ submitProductFeedbackTool.inputSchema.shape,
623
+ getToolAnnotations(submitProductFeedbackTool.name),
624
+ async (input) => {
625
+ try {
626
+ const text = await submitProductFeedbackTool.execute(client, input as any);
627
+ return { content: [{ type: 'text' as const, text }] };
628
+ } catch (err: any) {
629
+ return { content: [{ type: 'text' as const, text: `Error: ${err.message}` }], isError: true };
630
+ }
631
+ }
632
+ );
633
+
618
634
  server.tool(
619
635
  validatePostTool.name,
620
636
  validatePostTool.description,
@@ -223,6 +223,26 @@ export type AgentFeedbackResponse = {
223
223
  request_id: string;
224
224
  };
225
225
 
226
+ export type ProductFeedbackPayload = {
227
+ category: 'bug' | 'idea' | 'feedback';
228
+ title: string;
229
+ description?: string;
230
+ client?: string;
231
+ confirm: true;
232
+ source?: 'mcp' | 'cli' | 'rest' | 'skill';
233
+ context?: {
234
+ request_id?: string;
235
+ related_tool?: string;
236
+ };
237
+ };
238
+
239
+ export type ProductFeedbackResponse = {
240
+ accepted: true;
241
+ feedback_id: number;
242
+ board_url: string;
243
+ request_id: string;
244
+ };
245
+
226
246
  export type BatchCreatePostsResponse = {
227
247
  posts: Array<CreatePostResponse & { index: number; replayed: boolean }>;
228
248
  errors: Array<{ index: number; status: number; error: string; validation_errors?: unknown }>;
@@ -1168,6 +1188,10 @@ export class PosterlyClient {
1168
1188
  return this.request('POST', '/agent-feedback', data);
1169
1189
  }
1170
1190
 
1191
+ async submitProductFeedback(data: ProductFeedbackPayload): Promise<ProductFeedbackResponse> {
1192
+ return this.request('POST', '/feedback', data);
1193
+ }
1194
+
1171
1195
  async createPostsBatch(data: { posts: CreatePostPayload[] }): Promise<BatchCreatePostsResponse> {
1172
1196
  return this.request('POST', '/posts/batch', data);
1173
1197
  }
@@ -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.30.0';
8
+ export const POSTERLY_MCP_VERSION = '0.32.0';
@@ -0,0 +1,56 @@
1
+ import { z } from 'zod';
2
+ import type { PosterlyClient } from '../lib/api-client.js';
3
+
4
+ const tokenSchema = z.string().regex(/^[a-z0-9][a-z0-9_.:-]{0,63}$/);
5
+ const printableSchema = (maxLength: number) =>
6
+ z.string().trim().min(1).max(maxLength).refine(
7
+ (value) => !/[\u0000-\u001f\u007f]/.test(value),
8
+ 'Control characters are not allowed',
9
+ );
10
+
11
+ const secretFreeSchema = (maxLength: number) =>
12
+ printableSchema(maxLength).refine(
13
+ (value) => !/(?:pst_(?:live|test)_|sk-[a-z0-9_-]{12,})/i.test(value),
14
+ 'Must not contain secrets',
15
+ );
16
+
17
+ export const submitProductFeedbackInputSchema = z.object({
18
+ category: z.enum(['bug', 'idea', 'feedback']),
19
+ title: secretFreeSchema(120).refine((value) => value.length >= 3, 'Title must be at least 3 characters'),
20
+ description: secretFreeSchema(4000).optional(),
21
+ client: printableSchema(80).optional(),
22
+ confirm: z.literal(true),
23
+ source: z.enum(['mcp', 'cli', 'rest', 'skill']).optional(),
24
+ context: z
25
+ .object({
26
+ request_id: z.string().regex(/^[a-zA-Z0-9_.:-]{8,80}$/).optional(),
27
+ related_tool: tokenSchema.optional(),
28
+ })
29
+ .strict()
30
+ .optional(),
31
+ }).strict();
32
+
33
+ export type SubmitProductFeedbackInput = z.infer<typeof submitProductFeedbackInputSchema>;
34
+
35
+ export const submitProductFeedbackTool = {
36
+ name: 'submit_product_feedback',
37
+ description:
38
+ 'File a product bug, idea, or general feedback on the public posterly feedback board (same board users see in the dashboard). Only call this when the human explicitly asks to report a bug, suggest a feature, or send feedback to posterly. Require confirm=true after they approve the title and category. Never use this for tool failures or retries - use submit_agent_feedback for private operational telemetry. Never include API keys, secrets, captions, media URLs, or personal data about third parties.',
39
+ inputSchema: submitProductFeedbackInputSchema,
40
+
41
+ async execute(client: PosterlyClient, input: SubmitProductFeedbackInput) {
42
+ if (input.confirm !== true) {
43
+ return 'Refusing to file product feedback without confirm=true after explicit user approval of the title and category.';
44
+ }
45
+ const result = await client.submitProductFeedback({
46
+ ...input,
47
+ source: input.source || 'mcp',
48
+ });
49
+ return [
50
+ 'Product feedback filed on the public board.',
51
+ `• Feedback ID: ${result.feedback_id}`,
52
+ `• Board: ${result.board_url}`,
53
+ `• Request ID: ${result.request_id}`,
54
+ ].join('\n');
55
+ },
56
+ };
@@ -35,6 +35,7 @@
35
35
  "list_post_suggestions",
36
36
  "dismiss_suggestion",
37
37
  "submit_agent_feedback",
38
+ "submit_product_feedback",
38
39
  "validate_post",
39
40
  "create_post",
40
41
  "create_posts_batch",