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 +2 -1
- package/dist/index.js +10 -0
- package/dist/lib/api-client.d.ts +19 -0
- package/dist/lib/api-client.js +3 -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 +2 -2
- package/dist/tools/submit-product-feedback.d.ts +88 -0
- package/dist/tools/submit-product-feedback.js +39 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/index.ts +16 -0
- package/src/lib/api-client.ts +24 -0
- package/src/lib/version.ts +1 -1
- package/src/tools/submit-product-feedback.ts +56 -0
- package/tool-annotations.json +1 -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.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);
|
package/dist/lib/api-client.d.ts
CHANGED
|
@@ -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>;
|
package/dist/lib/api-client.js
CHANGED
|
@@ -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
|
}
|
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.32.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.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.
|
|
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.
|
|
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.
|
|
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,
|
package/src/lib/api-client.ts
CHANGED
|
@@ -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
|
}
|
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.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
|
+
};
|