@fil-technology/appmate-mcp 0.4.0 → 0.5.1

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
@@ -76,6 +76,24 @@ staging or self-hosted instances.
76
76
  | `update_report_draft` | Replace the report draft (categorised). |
77
77
  | `publish_report_flow` | Promote the report draft live. |
78
78
  | `list_report_submissions` | Paginated, optional `category` filter. |
79
+ | `get_contact_flow` | Read published + draft contact config. |
80
+ | `update_contact_draft` | Replace the contact draft. |
81
+ | `publish_contact_flow` | Promote the contact draft live. |
82
+ | `list_contact_submissions` | Paginated list of contact rows (name + email + message). |
83
+ | `get_onboarding_flow` | Read published + draft onboarding (web-to-app funnel) config. |
84
+ | `update_onboarding_draft` | Replace the onboarding draft (quiz / info / email-capture steps + handoff). |
85
+ | `publish_onboarding_flow` | Promote the onboarding draft live. |
86
+ | `list_onboarding_submissions` | Paginated list of funnel completions (answers + email + claim status). |
87
+ | `export_onboarding_csv` | Return all onboarding completions as a CSV string. |
88
+ | `get_referral_flow` | Read published + draft referral program config. |
89
+ | `update_referral_draft` | Replace the referral draft (rewards, share text, cap, landing). |
90
+ | `publish_referral_flow` | Promote the referral draft live. |
91
+ | `list_referrals` | Paginated referral graph (status, `source` [link or typed-code], referee, reward flags). |
92
+ | `export_referrals_csv` | Return the full referral graph as a CSV string. |
93
+
94
+ Each referrer gets a unique link **and** a short, human-readable code (e.g.
95
+ `K7Q4-R9XP`); a friend redeems by tapping the link or **typing the code** (no
96
+ clipboard needed). `source` on each referral row records which path was used.
79
97
 
80
98
  Tools that accept an app reference (`get_app`, `update_cancel_draft`,
81
99
  etc.) accept either the cuid `id` or the human-readable `slug` — use
@@ -91,6 +109,10 @@ whichever you have. The full REST shape is documented at
91
109
  > *"Export the waitlist for `appmate-pro` as CSV and save it to
92
110
  > `~/Downloads/waitlist.csv`."*
93
111
 
112
+ > *"Build a 3-question onboarding funnel for `ledgr` that asks the user's
113
+ > goal, captures their email, and hands off to the App Store, then publish
114
+ > it."*
115
+
94
116
  > *"Compare the published and draft cancel configs for `quakemate` and
95
117
  > tell me what changed."*
96
118
 
package/dist/index.js CHANGED
@@ -124,6 +124,18 @@ function zodToJsonSchema(schema) {
124
124
  if (schema instanceof z.ZodBoolean) {
125
125
  return { type: "boolean" };
126
126
  }
127
+ // z.preprocess / z.transform wrap the real schema in ZodEffects — emit the
128
+ // inner schema's shape so e.g. a preprocessed record still advertises
129
+ // `type: object` to the host.
130
+ if (schema instanceof z.ZodEffects) {
131
+ return zodToJsonSchema(schema._def.schema);
132
+ }
133
+ // A record (open-ended object, e.g. a flow `config`) MUST advertise
134
+ // `type: object` — otherwise hosts may serialize the value to a JSON string
135
+ // and the server rejects it ("expected object, received string").
136
+ if (schema instanceof z.ZodRecord) {
137
+ return { type: "object", additionalProperties: true };
138
+ }
127
139
  if (schema instanceof z.ZodUnknown || schema instanceof z.ZodAny) {
128
140
  return {};
129
141
  }
package/dist/tools.js CHANGED
@@ -1,5 +1,35 @@
1
1
  import { z } from "zod";
2
2
  import { apiFetch, apiFetchText } from "./api-client.js";
3
+ // `config` for the update_*_draft tools is a full flow-config OBJECT. We
4
+ // must advertise it as `type: object` in the JSON schema (see zodToJsonSchema
5
+ // in index.ts) — otherwise an untyped field leads some MCP hosts to serialize
6
+ // it to a JSON string, which the server then rejects with
7
+ // `422: expected object, received string`.
8
+ //
9
+ // Belt-and-braces: we ALSO accept a JSON string and parse it here (via
10
+ // z.preprocess), since a few hosts stringify nested args regardless. Either
11
+ // way the server receives a real object, never `"{...}"`.
12
+ function parseJsonStringConfig(v) {
13
+ if (typeof v === "string") {
14
+ const trimmed = v.trim();
15
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
16
+ try {
17
+ return JSON.parse(trimmed);
18
+ }
19
+ catch {
20
+ // Not valid JSON — leave it; record validation reports a clear error.
21
+ }
22
+ }
23
+ }
24
+ return v;
25
+ }
26
+ // Shared input for every update_*_draft tool: an app reference + the full
27
+ // config object. The inner record makes `config` advertise `type: object`;
28
+ // the preprocess tolerates a stringified body.
29
+ const updateDraftInput = z.object({
30
+ appIdOrSlug: z.string().min(1),
31
+ config: z.preprocess(parseJsonStringConfig, z.record(z.string(), z.unknown())),
32
+ });
3
33
  // ─── Apps ───────────────────────────────────────────────────────────────────
4
34
  export const listApps = {
5
35
  name: "list_apps",
@@ -46,10 +76,11 @@ export const getCancelFlow = {
46
76
  inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
47
77
  handler: (input, cfg) => apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/cancel`),
48
78
  };
49
- // Config body is `z.unknown()` so we hand the raw JSON to the server,
50
- // which has the canonical Zod schema. The server returns 422 with paths
51
- // on validation errors AND a `warnings` array on success for soft
52
- // mismatches (e.g. showThanksScreen + a "Contact support" label).
79
+ // `config` is the full flow-config object (advertised as type:object via
80
+ // updateDraftInput so hosts pass structured JSON). The server holds the
81
+ // canonical Zod schema: it returns 422 with paths on validation errors AND a
82
+ // `warnings` array on success for soft mismatches (e.g. showThanksScreen + a
83
+ // "Contact support" label).
53
84
  export const updateCancelDraft = {
54
85
  name: "update_cancel_draft",
55
86
  description: [
@@ -101,10 +132,7 @@ export const updateCancelDraft = {
101
132
  "",
102
133
  "See https://docs.appmate.cloud/ai-agents for the full do/don't guide and examples.",
103
134
  ].join("\n"),
104
- inputSchema: z.object({
105
- appIdOrSlug: z.string().min(1),
106
- config: z.unknown(),
107
- }),
135
+ inputSchema: updateDraftInput,
108
136
  handler: (input, cfg) => apiFetch(cfg, "PUT", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/cancel`, input.config),
109
137
  };
110
138
  export const publishCancelFlow = {
@@ -170,10 +198,7 @@ export const updateWaitlistDraft = {
170
198
  "Server returns { ok:true, warnings: [...] } — check warnings before publishing.",
171
199
  "Common warnings: partial_cta (label without url, or vice versa).",
172
200
  ].join("\n"),
173
- inputSchema: z.object({
174
- appIdOrSlug: z.string().min(1),
175
- config: z.unknown(),
176
- }),
201
+ inputSchema: updateDraftInput,
177
202
  handler: (input, cfg) => apiFetch(cfg, "PUT", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/waitlist`, input.config),
178
203
  };
179
204
  export const publishWaitlistFlow = {
@@ -225,6 +250,7 @@ export const updateFeedbackDraft = {
225
250
  "Shape:",
226
251
  " {",
227
252
  " type: 'feedback',",
253
+ " layout: 'steps' | 'single', // optional, default 'steps' (multi-step: rating+message, then email). 'single' = one screen.",
228
254
  " intro: {",
229
255
  " title, subtitle,",
230
256
  " messagePlaceholder, // textarea placeholder",
@@ -253,10 +279,7 @@ export const updateFeedbackDraft = {
253
279
  "",
254
280
  "Server returns { ok:true, warnings: [] }. Warning rules will be added later — for now treat any non-empty array as advisory.",
255
281
  ].join("\n"),
256
- inputSchema: z.object({
257
- appIdOrSlug: z.string().min(1),
258
- config: z.unknown(),
259
- }),
282
+ inputSchema: updateDraftInput,
260
283
  handler: (input, cfg) => apiFetch(cfg, "PUT", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/feedback`, input.config),
261
284
  };
262
285
  export const publishFeedbackFlow = {
@@ -298,6 +321,7 @@ export const updateReportDraft = {
298
321
  "Shape:",
299
322
  " {",
300
323
  " type: 'report',",
324
+ " layout: 'steps' | 'single', // optional, default 'steps' (multi-step: category, then message+email). 'single' = one screen.",
301
325
  " intro: {",
302
326
  " title, subtitle,",
303
327
  " messagePlaceholder, // textarea placeholder",
@@ -318,10 +342,7 @@ export const updateReportDraft = {
318
342
  "",
319
343
  "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
344
  ].join("\n"),
321
- inputSchema: z.object({
322
- appIdOrSlug: z.string().min(1),
323
- config: z.unknown(),
324
- }),
345
+ inputSchema: updateDraftInput,
325
346
  handler: (input, cfg) => apiFetch(cfg, "PUT", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/report`, input.config),
326
347
  };
327
348
  export const publishReportFlow = {
@@ -351,25 +372,266 @@ export const listReportSubmissions = {
351
372
  return apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/report/submissions${tail}`);
352
373
  },
353
374
  };
375
+ // ─── Contact flow ───────────────────────────────────────────────────────────
376
+ export const getContactFlow = {
377
+ name: "get_contact_flow",
378
+ description: "Read the published and draft contact flow configs for an app. Contact flows host a minimal inquiry form (optional name + required/optional email + optional message text) at appmate.cloud/contact/{appSlug}.",
379
+ inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
380
+ handler: (input, cfg) => apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/contact`),
381
+ };
382
+ export const updateContactDraft = {
383
+ name: "update_contact_draft",
384
+ description: [
385
+ "Replace the draft contact config. Body MUST be a full contact config object (type: 'contact').",
386
+ "",
387
+ "Shape:",
388
+ " {",
389
+ " type: 'contact',",
390
+ " layout: 'steps' | 'single', // optional, default 'steps' (multi-step: message, then name+email). 'single' = one screen.",
391
+ " intro: {",
392
+ " title, subtitle,",
393
+ " submitLabel,",
394
+ " legal? // optional small print under the form",
395
+ " },",
396
+ " nameField?: { // OPTIONAL name widget",
397
+ " enabled: boolean,",
398
+ " placeholder?: 'Your name',",
399
+ " required?: boolean",
400
+ " },",
401
+ " emailField?: { // OPTIONAL/REQUIRED email input",
402
+ " enabled: boolean,",
403
+ " placeholder?: 'you@example.com',",
404
+ " required?: boolean",
405
+ " },",
406
+ " messageField?: { // OPTIONAL message textarea",
407
+ " enabled: boolean,",
408
+ " placeholder?: 'What is on your mind?',",
409
+ " required?: boolean",
410
+ " },",
411
+ " success: {",
412
+ " title, body,",
413
+ " ctaLabel?, ctaUrl? // optional follow-up button pair",
414
+ " },",
415
+ " hero?: { // dynamic landing theme config",
416
+ " theme?: 'minimal' | 'gradient' | 'dark' | 'side_by_side',",
417
+ " eyebrow?, accentColor?, titleFont?",
418
+ " }",
419
+ " }",
420
+ ].join("\n"),
421
+ inputSchema: updateDraftInput,
422
+ handler: (input, cfg) => apiFetch(cfg, "PUT", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/contact`, input.config),
423
+ };
424
+ export const publishContactFlow = {
425
+ name: "publish_contact_flow",
426
+ description: "Promote the draft contact config to the live published version. Visitors at appmate.cloud/contact/{appSlug} see the new version live immediately.",
427
+ inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
428
+ handler: (input, cfg) => apiFetch(cfg, "POST", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/contact/publish`),
429
+ };
430
+ export const listContactSubmissions = {
431
+ name: "list_contact_submissions",
432
+ description: "Paginated list of contact submissions for an app. Each row: { id, name, email, message, source, country, createdAt }. limit max 200, default 50; pass nextCursor back for next page.",
433
+ inputSchema: z.object({
434
+ appIdOrSlug: z.string().min(1),
435
+ limit: z.number().int().min(1).max(200).optional(),
436
+ cursor: z.string().optional(),
437
+ }),
438
+ handler: (input, cfg) => {
439
+ const qs = new URLSearchParams();
440
+ if (input.limit !== undefined)
441
+ qs.set("limit", String(input.limit));
442
+ if (input.cursor)
443
+ qs.set("cursor", input.cursor);
444
+ const tail = qs.toString() ? `?${qs.toString()}` : "";
445
+ return apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/contact/submissions${tail}`);
446
+ },
447
+ };
448
+ // ─── Onboarding flow (web-to-app funnel) ─────────────────────────────────────
449
+ export const getOnboardingFlow = {
450
+ name: "get_onboarding_flow",
451
+ description: "Read the published and draft onboarding flow configs for an app. Onboarding flows are web-to-app funnels (intro → quiz/info/email-capture steps → App Store handoff) hosted at appmate.cloud/onboarding/{appSlug}. Answers + email are captured server-side; the iOS SDK recovers them on first launch via a claim token.",
452
+ inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
453
+ handler: (input, cfg) => apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/onboarding`),
454
+ };
455
+ export const updateOnboardingDraft = {
456
+ name: "update_onboarding_draft",
457
+ description: [
458
+ "Replace the draft onboarding config. Body MUST be a full onboarding config object (type: 'onboarding').",
459
+ "",
460
+ "Shape:",
461
+ " {",
462
+ " type: 'onboarding',",
463
+ " intro: { title, subtitle, startLabel, eyebrow? },",
464
+ " steps: [ // REQUIRED 1–20, ordered",
465
+ " // question step — pick one or several options",
466
+ " {",
467
+ " kind: 'question', id: 'goal',",
468
+ " prompt: 'What brings you here?', subtitle?: '…',",
469
+ " selectMode?: 'single' | 'multi', // omit → single",
470
+ " autoAdvance?: true, // single only: tap advances",
471
+ " required?: true,",
472
+ " options: [ { id: 'save_time', label: 'Save time', emoji?: '⚡' } ]",
473
+ " },",
474
+ " // info step — copy/image screen, no input",
475
+ " { kind: 'info', id: 'value', title: '…', body: '…', imageUrl?, continueLabel? },",
476
+ " // email_capture step — the lead-capture moment",
477
+ " {",
478
+ " kind: 'email_capture', id: 'email',",
479
+ " title: '…', subtitle?, placeholder?, submitLabel?,",
480
+ " required?: true, // omit → required",
481
+ " legal?: '…'",
482
+ " }",
483
+ " ],",
484
+ " handoff: {",
485
+ " title, body, ctaLabel,",
486
+ " appStoreUrl?, // App Store / TestFlight URL; omit → no button",
487
+ " legal?",
488
+ " },",
489
+ " hero?: { theme?, eyebrow?, accentColor?, titleFont? }",
490
+ " }",
491
+ "",
492
+ "Step + option ids must be snake_case ([a-z][a-z0-9_]*) and unique. The server returns { ok:true, warnings:[...] } — warnings flag a missing email_capture step, a handoff with no appStoreUrl, duplicate ids, etc. Treat warnings as advisory.",
493
+ ].join("\n"),
494
+ inputSchema: updateDraftInput,
495
+ handler: (input, cfg) => apiFetch(cfg, "PUT", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/onboarding`, input.config),
496
+ };
497
+ export const publishOnboardingFlow = {
498
+ name: "publish_onboarding_flow",
499
+ description: "Promote the draft onboarding config to the live published version. Visitors at appmate.cloud/onboarding/{appSlug} see the new funnel immediately.",
500
+ inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
501
+ handler: (input, cfg) => apiFetch(cfg, "POST", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/onboarding/publish`),
502
+ };
503
+ export const listOnboardingSubmissions = {
504
+ name: "list_onboarding_submissions",
505
+ description: "Paginated list of completed onboarding funnels for an app. Each row: { id, answers, email, source, claimed, claimedAt, country, createdAt }. `claimed` is true once the install was matched back to the completion. limit max 200, default 50; pass nextCursor back for the next page.",
506
+ inputSchema: z.object({
507
+ appIdOrSlug: z.string().min(1),
508
+ limit: z.number().int().min(1).max(200).optional(),
509
+ cursor: z.string().optional(),
510
+ }),
511
+ handler: (input, cfg) => {
512
+ const qs = new URLSearchParams();
513
+ if (input.limit !== undefined)
514
+ qs.set("limit", String(input.limit));
515
+ if (input.cursor)
516
+ qs.set("cursor", input.cursor);
517
+ const tail = qs.toString() ? `?${qs.toString()}` : "";
518
+ return apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/onboarding/submissions${tail}`);
519
+ },
520
+ };
521
+ export const exportOnboardingCsv = {
522
+ name: "export_onboarding_csv",
523
+ description: "Return all completed onboarding funnels for an app as a CSV string (header row + one row per completion, with email + answers JSON + claim status). Useful for hand-off to a spreadsheet or mail merge.",
524
+ inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
525
+ handler: async (input, cfg) => {
526
+ const csv = await apiFetchText(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/onboarding/submissions.csv`);
527
+ return { csv };
528
+ },
529
+ };
530
+ // ─── Referral flow ───────────────────────────────────────────────────────────
531
+ export const getReferralFlow = {
532
+ name: "get_referral_flow",
533
+ description: "Read the published and draft referral program config for an app. Referral is a share-with-a-friend loop: each user gets a link (appmate.cloud/r/{code}) AND a short human-readable code (e.g. K7Q4-R9XP); a friend who taps the link or types the code, then installs, triggers a reward for both sides. Codes + the referral graph live server-side; the iOS SDK mints links/codes, attributes installs on first launch (clipboard handoff) or via a typed code, and reports rewards owed.",
534
+ inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
535
+ handler: (input, cfg) => apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/referral`),
536
+ };
537
+ export const updateReferralDraft = {
538
+ name: "update_referral_draft",
539
+ description: [
540
+ "Replace the draft referral config. Body MUST be a full referral config object (type: 'referral').",
541
+ "",
542
+ "Shape:",
543
+ " {",
544
+ " type: 'referral',",
545
+ " landing: { // the invite page a friend sees at /r/{code}",
546
+ " eyebrow?, title, subtitle, ctaLabel,",
547
+ " appStoreUrl?, // REQUIRED before go-live — the install button target",
548
+ " fallback?: 'app_store' | 'website', // where a not-installed friend goes (default app_store; 'website' uses the app's websiteUrl)",
549
+ " legal?",
550
+ " },",
551
+ " share: { messageTemplate }, // the referrer's share text; the link is appended automatically",
552
+ " rewards: {",
553
+ " referrerWeeks: 1, // free weeks the referrer earns per installed friend (1–8)",
554
+ " refereeEnabled: true, // also reward the NEW user on install?",
555
+ " refereeWeeks?: 1, // required when refereeEnabled (1–8)",
556
+ " referrerLabel?, refereeLabel? // human copy shown on the landing + returned to the SDK",
557
+ " },",
558
+ " maxRewardsPerReferrer?: 10 // lifetime cap per referrer; 0/omit = unlimited (farming risk)",
559
+ " }",
560
+ "",
561
+ "Reward trigger is the friend's INSTALL (attributed on first launch). The server returns { ok:true, warnings:[...] } — warnings flag a missing/placeholder appStoreUrl, refereeEnabled without refereeWeeks, and an absent cap. Treat warnings as advisory but fix them before publishing.",
562
+ ].join("\n"),
563
+ inputSchema: updateDraftInput,
564
+ handler: (input, cfg) => apiFetch(cfg, "PUT", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/referral`, input.config),
565
+ };
566
+ export const publishReferralFlow = {
567
+ name: "publish_referral_flow",
568
+ description: "Promote the draft referral config to the live published version. New share links + the invite landing use the new config immediately.",
569
+ inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
570
+ handler: (input, cfg) => apiFetch(cfg, "POST", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/referral/publish`),
571
+ };
572
+ export const listReferrals = {
573
+ name: "list_referrals",
574
+ description: "Paginated list of referrals for an app. Each row: { id, code, referrerUserId, status, source, refereeUserId, attributedAt, referrerRewarded, refereeRewarded, country, createdAt }. status='attributed' means the friend installed; source is 'link' (tapped share link) or 'code' (typed the referrer's code). Optional `status` filter; limit max 200, default 50.",
575
+ inputSchema: z.object({
576
+ appIdOrSlug: z.string().min(1),
577
+ status: z.enum(["pending", "attributed", "expired"]).optional(),
578
+ limit: z.number().int().min(1).max(200).optional(),
579
+ cursor: z.string().optional(),
580
+ }),
581
+ handler: (input, cfg) => {
582
+ const qs = new URLSearchParams();
583
+ if (input.status)
584
+ qs.set("status", input.status);
585
+ if (input.limit !== undefined)
586
+ qs.set("limit", String(input.limit));
587
+ if (input.cursor)
588
+ qs.set("cursor", input.cursor);
589
+ const tail = qs.toString() ? `?${qs.toString()}` : "";
590
+ return apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/referrals${tail}`);
591
+ },
592
+ };
593
+ export const exportReferralsCsv = {
594
+ name: "export_referrals_csv",
595
+ description: "Return the full referral graph for an app as a CSV string (one row per invite, with code, status, source (link/typed-code), referee, and reward flags).",
596
+ inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
597
+ handler: async (input, cfg) => {
598
+ const csv = await apiFetchText(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/referrals.csv`);
599
+ return { csv };
600
+ },
601
+ };
354
602
  // Registered alphabetically so `list_tools` reads predictably.
355
603
  export const ALL_TOOLS = [
356
604
  createApp,
605
+ exportOnboardingCsv,
606
+ exportReferralsCsv,
357
607
  exportWaitlistCsv,
358
608
  getApp,
359
609
  getCancelFlow,
610
+ getContactFlow,
360
611
  getFeedbackFlow,
612
+ getOnboardingFlow,
613
+ getReferralFlow,
361
614
  getReportFlow,
362
615
  getWaitlistFlow,
363
616
  listApps,
617
+ listContactSubmissions,
364
618
  listFeedbackSubmissions,
619
+ listOnboardingSubmissions,
620
+ listReferrals,
365
621
  listReportSubmissions,
366
622
  listWaitlistSignups,
367
623
  publishCancelFlow,
624
+ publishContactFlow,
368
625
  publishFeedbackFlow,
626
+ publishOnboardingFlow,
627
+ publishReferralFlow,
369
628
  publishReportFlow,
370
629
  publishWaitlistFlow,
371
630
  updateCancelDraft,
631
+ updateContactDraft,
372
632
  updateFeedbackDraft,
633
+ updateOnboardingDraft,
634
+ updateReferralDraft,
373
635
  updateReportDraft,
374
636
  updateWaitlistDraft,
375
637
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fil-technology/appmate-mcp",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
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": {