@fil-technology/appmate-mcp 0.5.1 → 0.6.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 -0
- package/dist/tools.js +217 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,6 +90,9 @@ staging or self-hosted instances.
|
|
|
90
90
|
| `publish_referral_flow` | Promote the referral draft live. |
|
|
91
91
|
| `list_referrals` | Paginated referral graph (status, `source` [link or typed-code], referee, reward flags). |
|
|
92
92
|
| `export_referrals_csv` | Return the full referral graph as a CSV string. |
|
|
93
|
+
| `get_link_page_flow` | Read published + draft link-page (link-in-bio) config. |
|
|
94
|
+
| `update_link_page_draft` | Replace the link-page draft (header, icon links, link list, theme). |
|
|
95
|
+
| `publish_link_page_flow` | Promote the link-page draft live (appmate.cloud/p/{appSlug}). |
|
|
93
96
|
|
|
94
97
|
Each referrer gets a unique link **and** a short, human-readable code (e.g.
|
|
95
98
|
`K7Q4-R9XP`); a friend redeems by tapping the link or **typing the code** (no
|
package/dist/tools.js
CHANGED
|
@@ -175,7 +175,7 @@ export const updateWaitlistDraft = {
|
|
|
175
175
|
" theme?: 'minimal' | 'gradient' | 'dark' | 'side_by_side',",
|
|
176
176
|
" eyebrow?: string, // short chip above title, e.g. 'Coming soon · Q1 2026'",
|
|
177
177
|
" accentColor?: string, // hex '#rrggbb'; tints button + chip + gradient blob",
|
|
178
|
-
" bullets?: [ // 0–5 value-prop cards under the form",
|
|
178
|
+
" bullets?: [ // 0–5 value-prop cards, stacked one per row under the form",
|
|
179
179
|
" { icon?: '✨', title: 'Fast', body?: 'Sub-second responses' }",
|
|
180
180
|
" ],",
|
|
181
181
|
" showCount?: boolean, // renders '{N} on the waitlist' pill (hides if <3 signups)",
|
|
@@ -427,6 +427,46 @@ export const publishContactFlow = {
|
|
|
427
427
|
inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
|
|
428
428
|
handler: (input, cfg) => apiFetch(cfg, "POST", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/contact/publish`),
|
|
429
429
|
};
|
|
430
|
+
// ─── Link page (link-in-bio) ────────────────────────────────────────────────
|
|
431
|
+
export const getLinkPageFlow = {
|
|
432
|
+
name: "get_link_page_flow",
|
|
433
|
+
description: "Read the published and draft link-page configs for an app. A link page is a shareable 'link-in-bio' page (app logo + title/description, a row of icon links, and a list of labeled links) at appmate.cloud/p/{appSlug} — drop it in an Instagram/TikTok bio.",
|
|
434
|
+
inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
|
|
435
|
+
handler: (input, cfg) => apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/link-page`),
|
|
436
|
+
};
|
|
437
|
+
export const updateLinkPageDraft = {
|
|
438
|
+
name: "update_link_page_draft",
|
|
439
|
+
description: [
|
|
440
|
+
"Replace the draft link-page config. Body MUST be a full link_page config object (type: 'link_page').",
|
|
441
|
+
"",
|
|
442
|
+
"Shape:",
|
|
443
|
+
" {",
|
|
444
|
+
" type: 'link_page',",
|
|
445
|
+
" header: { title, description? }, // logo comes from the app record",
|
|
446
|
+
" iconLinks: [ // top row of compact icon buttons (<=6)",
|
|
447
|
+
" { icon: 'ri:instagram', label: 'Instagram', url: 'https://…' } // icon = a brand 'ri:<key>' (instagram/tiktok/x/youtube/…), a lucide name (e.g. 'Globe'), or an emoji",
|
|
448
|
+
" ],",
|
|
449
|
+
" links: [ // main list of labeled links (<=25)",
|
|
450
|
+
" { label: 'Download on the App Store', sublabel?: 'iPhone & iPad', url: 'https://…' }",
|
|
451
|
+
" ],",
|
|
452
|
+
" legal?: string, // small print at the bottom",
|
|
453
|
+
" hero?: { // visual treatment",
|
|
454
|
+
" theme?: 'minimal' | 'gradient' | 'dark' | 'side_by_side',",
|
|
455
|
+
" eyebrow?, accentColor?, titleFont?",
|
|
456
|
+
" }",
|
|
457
|
+
" }",
|
|
458
|
+
"",
|
|
459
|
+
"No submissions — a link page is a published, versioned page. Publish with publish_link_page_flow.",
|
|
460
|
+
].join("\n"),
|
|
461
|
+
inputSchema: updateDraftInput,
|
|
462
|
+
handler: (input, cfg) => apiFetch(cfg, "PUT", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/link-page`, input.config),
|
|
463
|
+
};
|
|
464
|
+
export const publishLinkPageFlow = {
|
|
465
|
+
name: "publish_link_page_flow",
|
|
466
|
+
description: "Promote the draft link-page config to the live published version. Visitors at appmate.cloud/p/{appSlug} see the new version immediately.",
|
|
467
|
+
inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
|
|
468
|
+
handler: (input, cfg) => apiFetch(cfg, "POST", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/link-page/publish`),
|
|
469
|
+
};
|
|
430
470
|
export const listContactSubmissions = {
|
|
431
471
|
name: "list_contact_submissions",
|
|
432
472
|
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.",
|
|
@@ -599,20 +639,188 @@ export const exportReferralsCsv = {
|
|
|
599
639
|
return { csv };
|
|
600
640
|
},
|
|
601
641
|
};
|
|
642
|
+
// ─── Wishlist flow (feature-request board) ──────────────────────────────────
|
|
643
|
+
//
|
|
644
|
+
// Unlike every other flow (write-only), the wishlist is an INTERACTIVE board:
|
|
645
|
+
// ideas are read back, upvoted, commented on, and moderated. Beyond the usual
|
|
646
|
+
// get/update/publish trio it exposes moderation tools (set status, delete idea,
|
|
647
|
+
// reply, delete comment) — the first MUTATING admin tools in the set.
|
|
648
|
+
export const getWishlistFlow = {
|
|
649
|
+
name: "get_wishlist_flow",
|
|
650
|
+
description: "Read the published and draft wishlist (feature-request board) configs for an app. The board lives at appmate.cloud/wishlist/{appSlug}; visitors submit ideas, upvote, and comment, and the owner moderates status.",
|
|
651
|
+
inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
|
|
652
|
+
handler: (input, cfg) => apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/wishlist`),
|
|
653
|
+
};
|
|
654
|
+
export const updateWishlistDraft = {
|
|
655
|
+
name: "update_wishlist_draft",
|
|
656
|
+
description: [
|
|
657
|
+
"Replace the draft wishlist config. Body MUST be a full wishlist config object (type: 'wishlist').",
|
|
658
|
+
"",
|
|
659
|
+
"Shape:",
|
|
660
|
+
" {",
|
|
661
|
+
" type: 'wishlist',",
|
|
662
|
+
" intro: { title, subtitle, submitLabel, legal? },",
|
|
663
|
+
" identity?: { // who can participate + vote dedup",
|
|
664
|
+
" mode: 'anonymous' | 'email', // default 'anonymous' (dedup by cookie+IP)",
|
|
665
|
+
" requireEmailToSubmit?, requireEmailToVote?, requireEmailToComment? // email mode only",
|
|
666
|
+
" },",
|
|
667
|
+
" categories?: [ // OPTIONAL tags (0–12)",
|
|
668
|
+
" { id: 'feature', label: 'New feature', emoji?: '✨', hint?: '…' }",
|
|
669
|
+
" ],",
|
|
670
|
+
" statusLabels?: { // rename lifecycle stages on the board",
|
|
671
|
+
" open?, planned?, in_progress?, done?, declined?",
|
|
672
|
+
" },",
|
|
673
|
+
" submitForm?: { titlePlaceholder?, bodyPlaceholder? },",
|
|
674
|
+
" moderation?: { autoApprove?: false }, // false (default) = new ideas start pending/hidden",
|
|
675
|
+
" success: { title, body, ctaLabel?, ctaUrl? },",
|
|
676
|
+
" hero?: { theme?, eyebrow?, accentColor?, titleFont? }",
|
|
677
|
+
" }",
|
|
678
|
+
"",
|
|
679
|
+
"Server returns { ok:true, warnings: [] }.",
|
|
680
|
+
].join("\n"),
|
|
681
|
+
inputSchema: updateDraftInput,
|
|
682
|
+
handler: (input, cfg) => apiFetch(cfg, "PUT", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/wishlist`, input.config),
|
|
683
|
+
};
|
|
684
|
+
export const publishWishlistFlow = {
|
|
685
|
+
name: "publish_wishlist_flow",
|
|
686
|
+
description: "Promote the draft wishlist config to the live published version. Visitors at appmate.cloud/wishlist/{appSlug} see the new version immediately.",
|
|
687
|
+
inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
|
|
688
|
+
handler: (input, cfg) => apiFetch(cfg, "POST", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/flows/wishlist/publish`),
|
|
689
|
+
};
|
|
690
|
+
export const listWishlistIdeas = {
|
|
691
|
+
name: "list_wishlist_ideas",
|
|
692
|
+
description: "Paginated list of wishlist ideas for an app — ALL statuses (including pending review). Each row: { id, title, body, category, status, voteCount, commentCount, pinned, author, email, source, country, createdAt }. Optional status ('pending'|'open'|'planned'|'in_progress'|'done'|'declined') + category filters; sort 'votes' (default 'new'). limit max 200, default 50; pass nextCursor back for the next page.",
|
|
693
|
+
inputSchema: z.object({
|
|
694
|
+
appIdOrSlug: z.string().min(1),
|
|
695
|
+
status: z.string().optional(),
|
|
696
|
+
category: z.string().optional(),
|
|
697
|
+
sort: z.enum(["votes", "new"]).optional(),
|
|
698
|
+
limit: z.number().int().min(1).max(200).optional(),
|
|
699
|
+
cursor: z.string().optional(),
|
|
700
|
+
}),
|
|
701
|
+
handler: (input, cfg) => {
|
|
702
|
+
const qs = new URLSearchParams();
|
|
703
|
+
if (input.status)
|
|
704
|
+
qs.set("status", input.status);
|
|
705
|
+
if (input.category)
|
|
706
|
+
qs.set("category", input.category);
|
|
707
|
+
if (input.sort)
|
|
708
|
+
qs.set("sort", input.sort);
|
|
709
|
+
if (input.limit !== undefined)
|
|
710
|
+
qs.set("limit", String(input.limit));
|
|
711
|
+
if (input.cursor)
|
|
712
|
+
qs.set("cursor", input.cursor);
|
|
713
|
+
const tail = qs.toString() ? `?${qs.toString()}` : "";
|
|
714
|
+
return apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/wishlist/ideas${tail}`);
|
|
715
|
+
},
|
|
716
|
+
};
|
|
717
|
+
export const createWishlistIdea = {
|
|
718
|
+
name: "create_wishlist_idea",
|
|
719
|
+
description: "Seed an idea onto the board as the app owner (so it isn't empty for early visitors — they can then upvote/comment). Defaults to status 'open' (visible). Use a category id from the flow config when set.",
|
|
720
|
+
inputSchema: z.object({
|
|
721
|
+
appIdOrSlug: z.string().min(1),
|
|
722
|
+
title: z.string().min(1).max(120),
|
|
723
|
+
body: z.string().max(4000).optional(),
|
|
724
|
+
category: z.string().optional(),
|
|
725
|
+
status: z
|
|
726
|
+
.enum(["pending", "open", "planned", "in_progress", "done", "declined"])
|
|
727
|
+
.optional(),
|
|
728
|
+
}),
|
|
729
|
+
handler: (input, cfg) => apiFetch(cfg, "POST", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/wishlist/ideas`, {
|
|
730
|
+
title: input.title,
|
|
731
|
+
body: input.body,
|
|
732
|
+
category: input.category,
|
|
733
|
+
status: input.status,
|
|
734
|
+
}),
|
|
735
|
+
};
|
|
736
|
+
export const setWishlistIdeaStatus = {
|
|
737
|
+
name: "set_wishlist_idea_status",
|
|
738
|
+
description: "Moderate an idea: set its status and/or pin it. 'pending' hides it from the public board; 'open' approves it. Statuses: pending | open | planned | in_progress | done | declined.",
|
|
739
|
+
inputSchema: z.object({
|
|
740
|
+
appIdOrSlug: z.string().min(1),
|
|
741
|
+
ideaId: z.string().min(1),
|
|
742
|
+
status: z.enum(["pending", "open", "planned", "in_progress", "done", "declined"]),
|
|
743
|
+
pinned: z.boolean().optional(),
|
|
744
|
+
}),
|
|
745
|
+
handler: (input, cfg) => apiFetch(cfg, "PATCH", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/wishlist/ideas/${encodeURIComponent(input.ideaId)}`, { status: input.status, ...(input.pinned !== undefined ? { pinned: input.pinned } : {}) }),
|
|
746
|
+
};
|
|
747
|
+
export const deleteWishlistIdea = {
|
|
748
|
+
name: "delete_wishlist_idea",
|
|
749
|
+
description: "Permanently delete an idea AND all of its votes and comments. IRREVERSIBLE — there is no undo. Prefer set_wishlist_idea_status with 'declined' to reject an idea while keeping it (and the decision) visible.",
|
|
750
|
+
inputSchema: z.object({
|
|
751
|
+
appIdOrSlug: z.string().min(1),
|
|
752
|
+
ideaId: z.string().min(1),
|
|
753
|
+
}),
|
|
754
|
+
handler: (input, cfg) => apiFetch(cfg, "DELETE", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/wishlist/ideas/${encodeURIComponent(input.ideaId)}`),
|
|
755
|
+
};
|
|
756
|
+
export const listWishlistComments = {
|
|
757
|
+
name: "list_wishlist_comments",
|
|
758
|
+
description: "Paginated comments on an idea (oldest-first, excludes deleted). Each row: { id, body, author, isOwner, isOfficial, createdAt }. limit max 200, default 50; pass nextCursor back for more.",
|
|
759
|
+
inputSchema: z.object({
|
|
760
|
+
appIdOrSlug: z.string().min(1),
|
|
761
|
+
ideaId: z.string().min(1),
|
|
762
|
+
limit: z.number().int().min(1).max(200).optional(),
|
|
763
|
+
cursor: z.string().optional(),
|
|
764
|
+
}),
|
|
765
|
+
handler: (input, cfg) => {
|
|
766
|
+
const qs = new URLSearchParams();
|
|
767
|
+
if (input.limit !== undefined)
|
|
768
|
+
qs.set("limit", String(input.limit));
|
|
769
|
+
if (input.cursor)
|
|
770
|
+
qs.set("cursor", input.cursor);
|
|
771
|
+
const tail = qs.toString() ? `?${qs.toString()}` : "";
|
|
772
|
+
return apiFetch(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/wishlist/ideas/${encodeURIComponent(input.ideaId)}/comments${tail}`);
|
|
773
|
+
},
|
|
774
|
+
};
|
|
775
|
+
export const postWishlistComment = {
|
|
776
|
+
name: "post_wishlist_comment",
|
|
777
|
+
description: "Post an OWNER reply on an idea (shown with a Team badge). Set official:true to mark it an authoritative response. Use this to answer or expand on a user's idea.",
|
|
778
|
+
inputSchema: z.object({
|
|
779
|
+
appIdOrSlug: z.string().min(1),
|
|
780
|
+
ideaId: z.string().min(1),
|
|
781
|
+
body: z.string().min(1).max(2000),
|
|
782
|
+
official: z.boolean().optional(),
|
|
783
|
+
}),
|
|
784
|
+
handler: (input, cfg) => apiFetch(cfg, "POST", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/wishlist/ideas/${encodeURIComponent(input.ideaId)}/comments`, { body: input.body, official: input.official ?? false }),
|
|
785
|
+
};
|
|
786
|
+
export const deleteWishlistComment = {
|
|
787
|
+
name: "delete_wishlist_comment",
|
|
788
|
+
description: "Soft-delete a comment (hidden from the public board, thread continuity preserved). Use for moderation.",
|
|
789
|
+
inputSchema: z.object({
|
|
790
|
+
appIdOrSlug: z.string().min(1),
|
|
791
|
+
commentId: z.string().min(1),
|
|
792
|
+
}),
|
|
793
|
+
handler: (input, cfg) => apiFetch(cfg, "DELETE", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/wishlist/comments/${encodeURIComponent(input.commentId)}`),
|
|
794
|
+
};
|
|
795
|
+
export const exportWishlistCsv = {
|
|
796
|
+
name: "export_wishlist_csv",
|
|
797
|
+
description: "Return all wishlist ideas for an app as a CSV string (one row per idea, every status: created_at, status, title, votes, comments, category, email, author, country, body).",
|
|
798
|
+
inputSchema: z.object({ appIdOrSlug: z.string().min(1) }),
|
|
799
|
+
handler: async (input, cfg) => {
|
|
800
|
+
const csv = await apiFetchText(cfg, "GET", `/api/v1/apps/${encodeURIComponent(input.appIdOrSlug)}/wishlist/ideas.csv`);
|
|
801
|
+
return { csv };
|
|
802
|
+
},
|
|
803
|
+
};
|
|
602
804
|
// Registered alphabetically so `list_tools` reads predictably.
|
|
603
805
|
export const ALL_TOOLS = [
|
|
604
806
|
createApp,
|
|
807
|
+
createWishlistIdea,
|
|
808
|
+
deleteWishlistComment,
|
|
809
|
+
deleteWishlistIdea,
|
|
605
810
|
exportOnboardingCsv,
|
|
606
811
|
exportReferralsCsv,
|
|
607
812
|
exportWaitlistCsv,
|
|
813
|
+
exportWishlistCsv,
|
|
608
814
|
getApp,
|
|
609
815
|
getCancelFlow,
|
|
610
816
|
getContactFlow,
|
|
611
817
|
getFeedbackFlow,
|
|
818
|
+
getLinkPageFlow,
|
|
612
819
|
getOnboardingFlow,
|
|
613
820
|
getReferralFlow,
|
|
614
821
|
getReportFlow,
|
|
615
822
|
getWaitlistFlow,
|
|
823
|
+
getWishlistFlow,
|
|
616
824
|
listApps,
|
|
617
825
|
listContactSubmissions,
|
|
618
826
|
listFeedbackSubmissions,
|
|
@@ -620,18 +828,26 @@ export const ALL_TOOLS = [
|
|
|
620
828
|
listReferrals,
|
|
621
829
|
listReportSubmissions,
|
|
622
830
|
listWaitlistSignups,
|
|
831
|
+
listWishlistComments,
|
|
832
|
+
listWishlistIdeas,
|
|
833
|
+
postWishlistComment,
|
|
623
834
|
publishCancelFlow,
|
|
624
835
|
publishContactFlow,
|
|
625
836
|
publishFeedbackFlow,
|
|
837
|
+
publishLinkPageFlow,
|
|
626
838
|
publishOnboardingFlow,
|
|
627
839
|
publishReferralFlow,
|
|
628
840
|
publishReportFlow,
|
|
629
841
|
publishWaitlistFlow,
|
|
842
|
+
publishWishlistFlow,
|
|
843
|
+
setWishlistIdeaStatus,
|
|
630
844
|
updateCancelDraft,
|
|
631
845
|
updateContactDraft,
|
|
632
846
|
updateFeedbackDraft,
|
|
847
|
+
updateLinkPageDraft,
|
|
633
848
|
updateOnboardingDraft,
|
|
634
849
|
updateReferralDraft,
|
|
635
850
|
updateReportDraft,
|
|
636
851
|
updateWaitlistDraft,
|
|
852
|
+
updateWishlistDraft,
|
|
637
853
|
];
|
package/package.json
CHANGED