@fil-technology/appmate-mcp 0.3.0 → 0.4.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.
Files changed (3) hide show
  1. package/README.md +8 -0
  2. package/dist/tools.js +185 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -68,6 +68,14 @@ staging or self-hosted instances.
68
68
  | `publish_waitlist_flow` | Promote the waitlist draft live. |
69
69
  | `list_waitlist_signups` | Paginated list (cursor + nextCursor). |
70
70
  | `export_waitlist_csv` | Return the full waitlist as a CSV string. |
71
+ | `get_feedback_flow` | Read published + draft feedback config. |
72
+ | `update_feedback_draft` | Replace the feedback draft. |
73
+ | `publish_feedback_flow` | Promote the feedback draft live. |
74
+ | `list_feedback_submissions` | Paginated list of feedback rows (rating + message + email). |
75
+ | `get_report_flow` | Read published + draft report config. |
76
+ | `update_report_draft` | Replace the report draft (categorised). |
77
+ | `publish_report_flow` | Promote the report draft live. |
78
+ | `list_report_submissions` | Paginated, optional `category` filter. |
71
79
 
72
80
  Tools that accept an app reference (`get_app`, `update_cancel_draft`,
73
81
  etc.) accept either the cuid `id` or the human-readable `slug` — use
package/dist/tools.js CHANGED
@@ -125,22 +125,50 @@ export const updateWaitlistDraft = {
125
125
  description: [
126
126
  "Replace the draft waitlist config. Body MUST be a full waitlist config object (type: 'waitlist').",
127
127
  "",
128
- "Required shape:",
128
+ "The public URL at signup.appmate.cloud/{slug} renders a FULL landing",
129
+ "page — not just a form. The `hero` block drives the visual treatment;",
130
+ "omit it for the minimal/legacy look.",
131
+ "",
132
+ "Full shape:",
129
133
  " {",
130
134
  " type: 'waitlist',",
131
135
  " intro: {",
132
- " title, subtitle,",
133
- " emailPlaceholder, submitLabel,",
134
- " legal? (small print, optional)",
136
+ " title, // h1 on the landing",
137
+ " subtitle, // lede paragraph",
138
+ " emailPlaceholder, // input placeholder, e.g. 'you@example.com'",
139
+ " submitLabel, // button text, e.g. 'Notify me'",
140
+ " legal? // small print under the form (optional)",
135
141
  " },",
136
142
  " success: {",
137
- " title, body,",
138
- " ctaLabel?, ctaUrl? // both-or-neither: ctaLabel without ctaUrl renders nothing",
139
- " }",
143
+ " title, body, // shown after signup",
144
+ " ctaLabel?, ctaUrl? // both-or-neither partial pair renders nothing",
145
+ " },",
146
+ " hero?: { // ALL fields optional; omit `hero` entirely for minimal",
147
+ " theme?: 'minimal' | 'gradient' | 'dark' | 'side_by_side',",
148
+ " eyebrow?: string, // short chip above title, e.g. 'Coming soon · Q1 2026'",
149
+ " accentColor?: string, // hex '#rrggbb'; tints button + chip + gradient blob",
150
+ " bullets?: [ // 0–5 value-prop cards under the form",
151
+ " { icon?: '✨', title: 'Fast', body?: 'Sub-second responses' }",
152
+ " ],",
153
+ " showCount?: boolean, // renders '{N} on the waitlist' pill (hides if <3 signups)",
154
+ " heroImage?: string, // optional URL of a hero image",
155
+ " },",
156
+ " templateId?: string // analytics tag if seeded from a template",
140
157
  " }",
141
158
  "",
159
+ "Theme picker guide — pick from intent:",
160
+ " - minimal → no brand, conservative B2B. Default.",
161
+ " - gradient → marketing-launch energy. Pastel blobs + accent color.",
162
+ " - dark → premium / product-reveal vibe. Dark hero + accent glow.",
163
+ " - side_by_side → desktop-first 2-column (story left, form right).",
164
+ " Collapses to minimal on phone — fine for landing-pageiness.",
165
+ "",
166
+ "Starter templates available (see /examples?kind=waitlist):",
167
+ " minimal_email_only, feature_tease, launching_soon, pro_upsell,",
168
+ " private_beta, early_access_referral.",
169
+ "",
142
170
  "Server returns { ok:true, warnings: [...] } — check warnings before publishing.",
143
- "Common: partial_cta (label without url, or vice versa).",
171
+ "Common warnings: partial_cta (label without url, or vice versa).",
144
172
  ].join("\n"),
145
173
  inputSchema: z.object({
146
174
  appIdOrSlug: z.string().min(1),
@@ -182,17 +210,166 @@ export const exportWaitlistCsv = {
182
210
  return { csv };
183
211
  },
184
212
  };
213
+ // ─── Feedback flow ──────────────────────────────────────────────────────────
214
+ export const getFeedbackFlow = {
215
+ name: "get_feedback_flow",
216
+ description: "Read the published and draft feedback flow configs for an app. Feedback flows host an open-ended message form (optional 1–5 star rating + optional reply email) at appmate.cloud/feedback/{appSlug}.",
217
+ inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
218
+ handler: (input, cfg) => apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/feedback`),
219
+ };
220
+ export const updateFeedbackDraft = {
221
+ name: "update_feedback_draft",
222
+ description: [
223
+ "Replace the draft feedback config. Body MUST be a full feedback config object (type: 'feedback').",
224
+ "",
225
+ "Shape:",
226
+ " {",
227
+ " type: 'feedback',",
228
+ " intro: {",
229
+ " title, subtitle,",
230
+ " messagePlaceholder, // textarea placeholder",
231
+ " submitLabel,",
232
+ " legal? // small print under the form (optional)",
233
+ " },",
234
+ " rating?: { // OPTIONAL 1–5 star widget",
235
+ " enabled: true,",
236
+ " prompt?: 'How would you rate your experience?',",
237
+ " required?: false // when true, blocks submit until picked",
238
+ " },",
239
+ " emailField?: { // OPTIONAL reply-to email field",
240
+ " enabled: true,",
241
+ " placeholder?: 'you@example.com (optional)',",
242
+ " required?: false",
243
+ " },",
244
+ " success: {",
245
+ " title, body,",
246
+ " ctaLabel?, ctaUrl? // both-or-neither follow-up CTA",
247
+ " },",
248
+ " hero?: { // visual treatment, matches waitlist hero",
249
+ " theme?: 'minimal' | 'gradient' | 'dark' | 'side_by_side',",
250
+ " eyebrow?, accentColor?, titleFont?",
251
+ " }",
252
+ " }",
253
+ "",
254
+ "Server returns { ok:true, warnings: [] }. Warning rules will be added later — for now treat any non-empty array as advisory.",
255
+ ].join("\n"),
256
+ inputSchema: z.object({
257
+ appIdOrSlug: z.string().min(1),
258
+ config: z.unknown(),
259
+ }),
260
+ handler: (input, cfg) => apiFetch(cfg, "PUT", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/feedback`, input.config),
261
+ };
262
+ export const publishFeedbackFlow = {
263
+ name: "publish_feedback_flow",
264
+ description: "Promote the draft feedback config to the live published version. Visitors at appmate.cloud/feedback/{appSlug} see the new version immediately.",
265
+ inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
266
+ handler: (input, cfg) => apiFetch(cfg, "POST", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/feedback/publish`),
267
+ };
268
+ export const listFeedbackSubmissions = {
269
+ name: "list_feedback_submissions",
270
+ description: "Paginated list of feedback submissions for an app. Each row: { id, message, rating, email, source, createdAt }. limit max 200, default 50; pass nextCursor back for the next page.",
271
+ inputSchema: z.object({
272
+ appIdOrSlug: z.string().min(1),
273
+ limit: z.number().int().min(1).max(200).optional(),
274
+ cursor: z.string().optional(),
275
+ }),
276
+ handler: (input, cfg) => {
277
+ const qs = new URLSearchParams();
278
+ if (input.limit !== undefined)
279
+ qs.set("limit", String(input.limit));
280
+ if (input.cursor)
281
+ qs.set("cursor", input.cursor);
282
+ const tail = qs.toString() ? `?${qs.toString()}` : "";
283
+ return apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/feedback/submissions${tail}`);
284
+ },
285
+ };
286
+ // ─── Report flow ────────────────────────────────────────────────────────────
287
+ export const getReportFlow = {
288
+ name: "get_report_flow",
289
+ description: "Read the published and draft report flow configs for an app. Report flows host a categorised bug/abuse/spam form (required category picker + message + optional reply email) at appmate.cloud/report/{appSlug}.",
290
+ inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
291
+ handler: (input, cfg) => apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/report`),
292
+ };
293
+ export const updateReportDraft = {
294
+ name: "update_report_draft",
295
+ description: [
296
+ "Replace the draft report config. Body MUST be a full report config object (type: 'report').",
297
+ "",
298
+ "Shape:",
299
+ " {",
300
+ " type: 'report',",
301
+ " intro: {",
302
+ " title, subtitle,",
303
+ " messagePlaceholder, // textarea placeholder",
304
+ " submitLabel,",
305
+ " legal? // optional small print",
306
+ " },",
307
+ " categories: [ // REQUIRED 1–10 entries",
308
+ " { id: 'bug', label: 'Bug or crash', emoji?: '🐞', hint?: '…' },",
309
+ " { id: 'abuse', label: 'Harassment or abuse', emoji?: '🚫' },",
310
+ " { id: 'spam', label: 'Spam', emoji?: '🧹' },",
311
+ " { id: 'privacy', label: 'Privacy concern', emoji?: '🔒' },",
312
+ " { id: 'other', label: 'Something else', emoji?: '💬' }",
313
+ " ],",
314
+ " emailField?: { enabled, placeholder?, required? },",
315
+ " success: { title, body, ctaLabel?, ctaUrl? },",
316
+ " hero?: { theme?, eyebrow?, accentColor?, titleFont? }",
317
+ " }",
318
+ "",
319
+ "Category ids must be snake_case ([a-z][a-z0-9_]*). The public submit endpoint validates posted category against this list — unknown ids return 422.",
320
+ ].join("\n"),
321
+ inputSchema: z.object({
322
+ appIdOrSlug: z.string().min(1),
323
+ config: z.unknown(),
324
+ }),
325
+ handler: (input, cfg) => apiFetch(cfg, "PUT", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/report`, input.config),
326
+ };
327
+ export const publishReportFlow = {
328
+ name: "publish_report_flow",
329
+ description: "Promote the draft report config to the live published version. Visitors at appmate.cloud/report/{appSlug} see the new version immediately.",
330
+ inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
331
+ handler: (input, cfg) => apiFetch(cfg, "POST", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/report/publish`),
332
+ };
333
+ export const listReportSubmissions = {
334
+ name: "list_report_submissions",
335
+ description: "Paginated list of report submissions for an app. Each row: { id, message, category, email, source, createdAt }. Pass `category` to scope to one bucket (e.g. 'bug') for triage.",
336
+ inputSchema: z.object({
337
+ appIdOrSlug: z.string().min(1),
338
+ limit: z.number().int().min(1).max(200).optional(),
339
+ cursor: z.string().optional(),
340
+ category: z.string().optional(),
341
+ }),
342
+ handler: (input, cfg) => {
343
+ const qs = new URLSearchParams();
344
+ if (input.limit !== undefined)
345
+ qs.set("limit", String(input.limit));
346
+ if (input.cursor)
347
+ qs.set("cursor", input.cursor);
348
+ if (input.category)
349
+ qs.set("category", input.category);
350
+ const tail = qs.toString() ? `?${qs.toString()}` : "";
351
+ return apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/report/submissions${tail}`);
352
+ },
353
+ };
185
354
  // Registered alphabetically so `list_tools` reads predictably.
186
355
  export const ALL_TOOLS = [
187
356
  createApp,
188
357
  exportWaitlistCsv,
189
358
  getApp,
190
359
  getCancelFlow,
360
+ getFeedbackFlow,
361
+ getReportFlow,
191
362
  getWaitlistFlow,
192
363
  listApps,
364
+ listFeedbackSubmissions,
365
+ listReportSubmissions,
193
366
  listWaitlistSignups,
194
367
  publishCancelFlow,
368
+ publishFeedbackFlow,
369
+ publishReportFlow,
195
370
  publishWaitlistFlow,
196
371
  updateCancelDraft,
372
+ updateFeedbackDraft,
373
+ updateReportDraft,
197
374
  updateWaitlistDraft,
198
375
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fil-technology/appmate-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Model Context Protocol server for AppMate — lets Claude / Cursor / Codex drive your retention flows via API tokens.",
5
5
  "type": "module",
6
6
  "bin": {