0nmcp 3.2.2 → 4.5.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/LICENSE +94 -21
- package/README.md +9 -8
- package/crm/addons.js +319 -0
- package/crm/agent-builder.js +223 -0
- package/crm/billing.js +173 -0
- package/crm/conversations.js +14 -0
- package/crm/course-generator.js +161 -0
- package/crm/email-campaigns.js +250 -0
- package/crm/helpers.js +9 -0
- package/crm/index.js +48 -2
- package/crm/marketplace-billing.js +162 -0
- package/crm/media.js +167 -0
- package/crm/oauth-store.js +262 -0
- package/crm/phone-system.js +88 -0
- package/crm/saas-management.js +72 -0
- package/crm/sdk.js +60 -0
- package/crm/supabase-session-storage.js +54 -0
- package/crm/surveys-forms.js +96 -0
- package/crm/user-context.js +103 -0
- package/lib/stats.json +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// 0nMCP — CRM Email Campaign Execution Tools
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Full email campaign platform:
|
|
5
|
+
// - Fetch & display CRM email templates
|
|
6
|
+
// - Create new templates
|
|
7
|
+
// - Fetch campaigns with stats
|
|
8
|
+
// - IP warm-up scheduling
|
|
9
|
+
// - Template selection for campaign execution
|
|
10
|
+
//
|
|
11
|
+
// SDK methods: emails.fetchCampaigns, emails.fetchTemplate,
|
|
12
|
+
// emails.createTemplate, emails.updateTemplate, emails.deleteTemplate
|
|
13
|
+
// campaigns.getCampaigns, conversations.sendANewMessage
|
|
14
|
+
// ============================================================
|
|
15
|
+
|
|
16
|
+
import getSDK from "./sdk.js";
|
|
17
|
+
import { crmHeaders, CRM_API_BASE } from "./helpers.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Register email campaign MCP tools.
|
|
21
|
+
*/
|
|
22
|
+
export function registerEmailCampaignTools(server, z) {
|
|
23
|
+
|
|
24
|
+
// ── List email templates ───────────────────────────────────
|
|
25
|
+
server.tool(
|
|
26
|
+
"crm_list_email_templates",
|
|
27
|
+
"List all email templates for a location. Returns template names, preview URLs, and IDs for selection in campaign builders.",
|
|
28
|
+
{
|
|
29
|
+
location_id: z.string().describe("CRM location ID"),
|
|
30
|
+
search: z.string().optional().describe("Search by template name"),
|
|
31
|
+
limit: z.number().optional().describe("Max results (default 20)"),
|
|
32
|
+
offset: z.number().optional().describe("Pagination offset"),
|
|
33
|
+
},
|
|
34
|
+
async ({ location_id, search, limit, offset }) => {
|
|
35
|
+
try {
|
|
36
|
+
const sdk = await getSDK();
|
|
37
|
+
const result = await sdk.emails.fetchTemplate({
|
|
38
|
+
locationId: location_id,
|
|
39
|
+
limit: String(limit || 20),
|
|
40
|
+
offset: String(offset || 0),
|
|
41
|
+
search: search || undefined,
|
|
42
|
+
templatesOnly: "true",
|
|
43
|
+
});
|
|
44
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
45
|
+
} catch (err) {
|
|
46
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: err.message }) }] };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// ── Create email template ──────────────────────────────────
|
|
52
|
+
server.tool(
|
|
53
|
+
"crm_create_email_template",
|
|
54
|
+
"Create a new email template. Returns a redirect URL to the CRM email builder where the template can be visually edited.",
|
|
55
|
+
{
|
|
56
|
+
location_id: z.string().describe("CRM location ID"),
|
|
57
|
+
name: z.string().describe("Template name"),
|
|
58
|
+
type: z.string().optional().describe("Template type (html, folder). Default: html"),
|
|
59
|
+
import_url: z.string().optional().describe("URL to import template from"),
|
|
60
|
+
},
|
|
61
|
+
async ({ location_id, name, type, import_url }) => {
|
|
62
|
+
try {
|
|
63
|
+
const sdk = await getSDK();
|
|
64
|
+
const result = await sdk.emails.createTemplate({
|
|
65
|
+
locationId: location_id,
|
|
66
|
+
name,
|
|
67
|
+
type: type || "html",
|
|
68
|
+
importProvider: import_url ? "url" : "blank",
|
|
69
|
+
importURL: import_url || undefined,
|
|
70
|
+
});
|
|
71
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
72
|
+
} catch (err) {
|
|
73
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: err.message }) }] };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// ── List campaigns with stats ──────────────────────────────
|
|
79
|
+
server.tool(
|
|
80
|
+
"crm_list_email_campaigns",
|
|
81
|
+
"List email campaigns for a location with optional stats. Shows campaign name, status, open/click rates.",
|
|
82
|
+
{
|
|
83
|
+
location_id: z.string().describe("CRM location ID"),
|
|
84
|
+
status: z.string().optional().describe("Filter by status (draft, scheduled, sent, archived)"),
|
|
85
|
+
show_stats: z.boolean().optional().describe("Include open/click stats (default true)"),
|
|
86
|
+
limit: z.number().optional().describe("Max results"),
|
|
87
|
+
offset: z.number().optional().describe("Pagination offset"),
|
|
88
|
+
},
|
|
89
|
+
async ({ location_id, status, show_stats, limit, offset }) => {
|
|
90
|
+
try {
|
|
91
|
+
const sdk = await getSDK();
|
|
92
|
+
const result = await sdk.emails.fetchCampaigns({
|
|
93
|
+
locationId: location_id,
|
|
94
|
+
status: status || undefined,
|
|
95
|
+
showStats: show_stats !== false,
|
|
96
|
+
limit: limit || 20,
|
|
97
|
+
offset: offset || 0,
|
|
98
|
+
campaignsOnly: true,
|
|
99
|
+
});
|
|
100
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
101
|
+
} catch (err) {
|
|
102
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: err.message }) }] };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// ── Send email to contact ──────────────────────────────────
|
|
108
|
+
server.tool(
|
|
109
|
+
"crm_send_email",
|
|
110
|
+
"Send an email to a contact. Supports HTML body, templates, attachments, CC/BCC, threading, and scheduling.",
|
|
111
|
+
{
|
|
112
|
+
access_token: z.string().optional().describe("CRM access token (auto-resolved if location_id provided)"),
|
|
113
|
+
location_id: z.string().optional().describe("CRM location ID"),
|
|
114
|
+
contact_id: z.string().describe("Contact ID to send to"),
|
|
115
|
+
subject: z.string().describe("Email subject line"),
|
|
116
|
+
html: z.string().optional().describe("HTML email body"),
|
|
117
|
+
message: z.string().optional().describe("Plain text message body"),
|
|
118
|
+
from_email: z.string().optional().describe("From email address"),
|
|
119
|
+
to_email: z.string().optional().describe("To email (overrides contact default)"),
|
|
120
|
+
cc: z.array(z.string()).optional().describe("CC email addresses"),
|
|
121
|
+
bcc: z.array(z.string()).optional().describe("BCC email addresses"),
|
|
122
|
+
template_id: z.string().optional().describe("CRM email template ID"),
|
|
123
|
+
attachments: z.array(z.string()).optional().describe("Attachment URLs"),
|
|
124
|
+
thread_id: z.string().optional().describe("Email thread ID for threading"),
|
|
125
|
+
reply_mode: z.string().optional().describe("Reply mode: reply, reply_all"),
|
|
126
|
+
scheduled_timestamp: z.number().optional().describe("Unix timestamp to schedule send"),
|
|
127
|
+
},
|
|
128
|
+
async (args) => {
|
|
129
|
+
try {
|
|
130
|
+
// Use direct API since conversations.sendANewMessage covers this
|
|
131
|
+
const token = args.access_token;
|
|
132
|
+
if (!token) {
|
|
133
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "access_token required for sending" }) }] };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const body = {
|
|
137
|
+
type: "Email",
|
|
138
|
+
contactId: args.contact_id,
|
|
139
|
+
subject: args.subject,
|
|
140
|
+
html: args.html || undefined,
|
|
141
|
+
message: args.message || undefined,
|
|
142
|
+
emailFrom: args.from_email || undefined,
|
|
143
|
+
emailTo: args.to_email || undefined,
|
|
144
|
+
emailCc: args.cc || undefined,
|
|
145
|
+
emailBcc: args.bcc || undefined,
|
|
146
|
+
templateId: args.template_id || undefined,
|
|
147
|
+
attachments: args.attachments || undefined,
|
|
148
|
+
threadId: args.thread_id || undefined,
|
|
149
|
+
emailReplyMode: args.reply_mode || undefined,
|
|
150
|
+
scheduledTimestamp: args.scheduled_timestamp || undefined,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const headers = crmHeaders(token);
|
|
154
|
+
const res = await fetch(`${CRM_API_BASE}/conversations/messages`, {
|
|
155
|
+
method: "POST",
|
|
156
|
+
headers,
|
|
157
|
+
body: JSON.stringify(body),
|
|
158
|
+
});
|
|
159
|
+
const data = await res.json();
|
|
160
|
+
|
|
161
|
+
return { content: [{ type: "text", text: JSON.stringify({ sent: res.ok, status: res.status, data }, null, 2) }] };
|
|
162
|
+
} catch (err) {
|
|
163
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: err.message }) }] };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// ── IP warm-up schedule generator ──────────────────────────
|
|
169
|
+
server.tool(
|
|
170
|
+
"crm_generate_warmup_schedule",
|
|
171
|
+
"Generate an IP warm-up schedule for a new email sending domain. Returns a day-by-day plan with volume targets, engagement monitoring, and escalation thresholds.",
|
|
172
|
+
{
|
|
173
|
+
daily_target: z.number().describe("Target daily send volume after warm-up"),
|
|
174
|
+
domain: z.string().describe("Sending domain being warmed up"),
|
|
175
|
+
warmup_days: z.number().optional().describe("Number of warm-up days (default 30)"),
|
|
176
|
+
},
|
|
177
|
+
async ({ daily_target, domain, warmup_days }) => {
|
|
178
|
+
const days = warmup_days || 30;
|
|
179
|
+
const schedule = [];
|
|
180
|
+
|
|
181
|
+
for (let day = 1; day <= days; day++) {
|
|
182
|
+
const pct = Math.min(1, Math.pow(day / days, 1.5));
|
|
183
|
+
const volume = Math.max(10, Math.round(daily_target * pct));
|
|
184
|
+
const phase = day <= 7 ? "cautious" : day <= 14 ? "building" : day <= 21 ? "accelerating" : "full";
|
|
185
|
+
|
|
186
|
+
schedule.push({
|
|
187
|
+
day,
|
|
188
|
+
volume,
|
|
189
|
+
phase,
|
|
190
|
+
engagement_target: phase === "cautious" ? ">30% open rate" : phase === "building" ? ">25% open rate" : ">20% open rate",
|
|
191
|
+
action: phase === "cautious"
|
|
192
|
+
? "Send to most engaged contacts only (opened in last 30 days)"
|
|
193
|
+
: phase === "building"
|
|
194
|
+
? "Expand to contacts who opened in last 90 days"
|
|
195
|
+
: phase === "accelerating"
|
|
196
|
+
? "Include contacts who opened in last 180 days"
|
|
197
|
+
: "Full list — monitor bounce rate (<2%) and spam rate (<0.1%)",
|
|
198
|
+
pause_if: "Bounce rate >5% OR spam complaints >0.3% OR open rate drops below 15%",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
content: [{
|
|
204
|
+
type: "text",
|
|
205
|
+
text: JSON.stringify({
|
|
206
|
+
domain,
|
|
207
|
+
total_days: days,
|
|
208
|
+
target_daily_volume: daily_target,
|
|
209
|
+
schedule,
|
|
210
|
+
rules: [
|
|
211
|
+
"Never send to unengaged contacts in first 14 days",
|
|
212
|
+
"Monitor postmaster tools daily (Google, Yahoo, Microsoft)",
|
|
213
|
+
"If any ISP blocks, pause 48 hours and reduce volume 50%",
|
|
214
|
+
"Authenticate SPF, DKIM, DMARC before starting",
|
|
215
|
+
"Use consistent from address and branding throughout",
|
|
216
|
+
],
|
|
217
|
+
}, null, 2),
|
|
218
|
+
}],
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
// ── Get template iframe URL ────────────────────────────────
|
|
224
|
+
server.tool(
|
|
225
|
+
"crm_get_template_preview_url",
|
|
226
|
+
"Get the preview URL for a CRM email template. This URL can be loaded in an iframe to show the template visually.",
|
|
227
|
+
{
|
|
228
|
+
location_id: z.string().describe("CRM location ID"),
|
|
229
|
+
template_id: z.string().describe("Template ID"),
|
|
230
|
+
},
|
|
231
|
+
async ({ location_id, template_id }) => {
|
|
232
|
+
// CRM template preview URLs follow this pattern
|
|
233
|
+
const previewUrl = `https://app.leadconnectorhq.com/v2/location/${location_id}/marketing/emails/editor/${template_id}`;
|
|
234
|
+
const thumbnailUrl = `https://services.leadconnectorhq.com/emails/builder/${template_id}/thumbnail?locationId=${location_id}`;
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
content: [{
|
|
238
|
+
type: "text",
|
|
239
|
+
text: JSON.stringify({
|
|
240
|
+
template_id,
|
|
241
|
+
location_id,
|
|
242
|
+
preview_url: previewUrl,
|
|
243
|
+
thumbnail_url: thumbnailUrl,
|
|
244
|
+
embed_note: "Use thumbnail_url in an img tag for preview. preview_url requires CRM auth to load in iframe.",
|
|
245
|
+
}, null, 2),
|
|
246
|
+
}],
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
}
|
package/crm/helpers.js
CHANGED
|
@@ -35,12 +35,21 @@ export function crmHeaders(accessToken) {
|
|
|
35
35
|
* Every tool automatically receives `access_token` (required) and
|
|
36
36
|
* `location_id` (optional) parameters.
|
|
37
37
|
*/
|
|
38
|
+
let _resolveToken = null;
|
|
39
|
+
export function setTokenResolver(fn) { _resolveToken = fn; }
|
|
40
|
+
|
|
38
41
|
export function registerTools(server, z, definitions, proxy) {
|
|
39
42
|
for (const def of definitions) {
|
|
40
43
|
const schema = buildSchema(z, def);
|
|
41
44
|
|
|
42
45
|
server.tool(def.name, def.description, schema, async (args) => {
|
|
43
46
|
try {
|
|
47
|
+
if (!args.access_token && args.location_id && _resolveToken) {
|
|
48
|
+
args.access_token = await _resolveToken(args.location_id);
|
|
49
|
+
}
|
|
50
|
+
if (!args.access_token) {
|
|
51
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "No access_token and no OAuth token for this location. Use crm_oauth_connect or pass access_token." }) }] };
|
|
52
|
+
}
|
|
44
53
|
const headers = crmHeaders(args.access_token);
|
|
45
54
|
let url = CRM_API_BASE + def.path;
|
|
46
55
|
|
package/crm/index.js
CHANGED
|
@@ -11,8 +11,18 @@
|
|
|
11
11
|
// crm/index.js — This file (wires everything together)
|
|
12
12
|
// ============================================================
|
|
13
13
|
|
|
14
|
-
import { registerTools, crmHeaders, CRM_API_BASE, API_VERSION } from "./helpers.js";
|
|
14
|
+
import { registerTools, crmHeaders, CRM_API_BASE, API_VERSION, setTokenResolver } from "./helpers.js";
|
|
15
15
|
import { registerAuthTools } from "./auth.js";
|
|
16
|
+
import { registerOAuthStoreTools, resolveToken } from "./oauth-store.js";
|
|
17
|
+
import { registerCourseTools } from "./course-generator.js";
|
|
18
|
+
import { registerUserContextTools } from "./user-context.js";
|
|
19
|
+
import { registerBillingTools } from "./billing.js";
|
|
20
|
+
import { registerEmailCampaignTools } from "./email-campaigns.js";
|
|
21
|
+
import { registerMarketplaceBillingTools } from "./marketplace-billing.js";
|
|
22
|
+
import { registerMediaTools } from "./media.js";
|
|
23
|
+
import { registerSaasManagementTools } from "./saas-management.js";
|
|
24
|
+
import { registerSurveyFormTools } from "./surveys-forms.js";
|
|
25
|
+
import { registerAgentBuilderTools } from "./agent-builder.js";
|
|
16
26
|
|
|
17
27
|
// Category modules — each exports a default array of tool definitions
|
|
18
28
|
import contacts from "./contacts.js";
|
|
@@ -30,6 +40,7 @@ import knowledgeBase from "./knowledge-base.js";
|
|
|
30
40
|
import voiceAi from "./voice-ai.js";
|
|
31
41
|
import saas from "./saas.js";
|
|
32
42
|
import funnels from "./funnels.js"; // includes forms, surveys, associations, snapshots
|
|
43
|
+
import phoneSystem from "./phone-system.js";
|
|
33
44
|
import agentStudio from "./agent-studio.js";
|
|
34
45
|
|
|
35
46
|
// Re-export definitions + helpers for external consumers (e.g. CRM bridges)
|
|
@@ -50,6 +61,7 @@ export {
|
|
|
50
61
|
saas,
|
|
51
62
|
funnels,
|
|
52
63
|
agentStudio,
|
|
64
|
+
resolveToken,
|
|
53
65
|
crmHeaders,
|
|
54
66
|
CRM_API_BASE,
|
|
55
67
|
API_VERSION,
|
|
@@ -63,9 +75,42 @@ export {
|
|
|
63
75
|
* @param {import("../capability-proxy.js").CapabilityProxy} [proxy]
|
|
64
76
|
*/
|
|
65
77
|
export function registerCrmTools(server, z, proxy) {
|
|
78
|
+
// 0. Wire up OAuth token auto-resolution
|
|
79
|
+
setTokenResolver(resolveToken);
|
|
80
|
+
|
|
66
81
|
// 1. Auth & custom-logic tools (OAuth, snapshot deploy, workflow process)
|
|
67
82
|
registerAuthTools(server, z);
|
|
68
83
|
|
|
84
|
+
// 1b. OAuth store tools (connect, status, resolve)
|
|
85
|
+
registerOAuthStoreTools(server, z);
|
|
86
|
+
|
|
87
|
+
// 1c. SDK-powered tools (courses, etc.)
|
|
88
|
+
registerCourseTools(server, z);
|
|
89
|
+
|
|
90
|
+
// 1d. User context decryption (marketplace app embeds)
|
|
91
|
+
registerUserContextTools(server, z);
|
|
92
|
+
|
|
93
|
+
// 1e. External billing (Stripe → CRM webhook) + payment config
|
|
94
|
+
registerBillingTools(server, z);
|
|
95
|
+
|
|
96
|
+
// 1f. Email campaign execution (templates, campaigns, warmup, send)
|
|
97
|
+
registerEmailCampaignTools(server, z);
|
|
98
|
+
|
|
99
|
+
// 1g. Marketplace billing (charge, rebilling, installer details)
|
|
100
|
+
registerMarketplaceBillingTools(server, z);
|
|
101
|
+
|
|
102
|
+
// 1h. Media library (upload, folders, browse, delete)
|
|
103
|
+
registerMediaTools(server, z);
|
|
104
|
+
|
|
105
|
+
// 1i. SaaS management (locations, plans, rebilling, payment links)
|
|
106
|
+
registerSaasManagementTools(server, z);
|
|
107
|
+
|
|
108
|
+
// 1j. Surveys & Forms (list, submissions, file upload)
|
|
109
|
+
registerSurveyFormTools(server, z);
|
|
110
|
+
|
|
111
|
+
// 1k. AI Agent Builder (create, list, execute AI Workflows)
|
|
112
|
+
registerAgentBuilderTools(server, z);
|
|
113
|
+
|
|
69
114
|
// 2. Data-driven tools by category
|
|
70
115
|
const categories = [
|
|
71
116
|
{ name: "Contacts", defs: contacts },
|
|
@@ -84,9 +129,10 @@ export function registerCrmTools(server, z, proxy) {
|
|
|
84
129
|
{ name: "SaaS & Billing", defs: saas },
|
|
85
130
|
{ name: "Funnels, Forms, Surveys & More", defs: funnels },
|
|
86
131
|
{ name: "Agent Studio", defs: agentStudio },
|
|
132
|
+
{ name: "Phone System", defs: phoneSystem },
|
|
87
133
|
];
|
|
88
134
|
|
|
89
|
-
let totalTools = 5; // auth
|
|
135
|
+
let totalTools = 5 + 3 + 2 + 1 + 2 + 6 + 7 + 6 + 3 + 3 + 3; // auth(5) + oauth-store(3) + course(2) + user-context(1) + billing(2) + email-campaigns(6) + marketplace-billing(7) + media(6) + saas-mgmt(3) + surveys-forms(3) + agent-builder(3)
|
|
90
136
|
for (const cat of categories) {
|
|
91
137
|
registerTools(server, z, cat.defs, proxy);
|
|
92
138
|
totalTools += cat.defs.length;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// 0nMCP — CRM Marketplace Billing & Rebilling Tools
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Full marketplace billing: charge locations, check funds,
|
|
5
|
+
// get installer details, rebilling config, uninstall.
|
|
6
|
+
//
|
|
7
|
+
// SDK methods: marketplace.charge, marketplace.getCharges,
|
|
8
|
+
// marketplace.hasFunds, marketplace.getInstallerDetails,
|
|
9
|
+
// marketplace.getSpecificCharge, marketplace.deleteCharge,
|
|
10
|
+
// marketplace.uninstallApplication
|
|
11
|
+
//
|
|
12
|
+
// REST: GET /marketplace/app/:appId/rebilling-config/location/:locationId
|
|
13
|
+
// ============================================================
|
|
14
|
+
|
|
15
|
+
import getSDK from "./sdk.js";
|
|
16
|
+
import { crmHeaders, CRM_API_BASE } from "./helpers.js";
|
|
17
|
+
|
|
18
|
+
export function registerMarketplaceBillingTools(server, z) {
|
|
19
|
+
|
|
20
|
+
server.tool(
|
|
21
|
+
"crm_marketplace_charge",
|
|
22
|
+
"Charge a location for an add-on or service through the CRM marketplace billing. The CRM handles payment collection from the sub-account owner.",
|
|
23
|
+
{
|
|
24
|
+
location_id: z.string().describe("CRM location ID to charge"),
|
|
25
|
+
amount: z.number().describe("Amount in cents"),
|
|
26
|
+
description: z.string().describe("Charge description"),
|
|
27
|
+
name: z.string().describe("Product/add-on name"),
|
|
28
|
+
},
|
|
29
|
+
async ({ location_id, amount, description, name }) => {
|
|
30
|
+
try {
|
|
31
|
+
const sdk = await getSDK();
|
|
32
|
+
const result = await sdk.marketplace.charge({
|
|
33
|
+
locationId: location_id,
|
|
34
|
+
amount,
|
|
35
|
+
description,
|
|
36
|
+
name,
|
|
37
|
+
});
|
|
38
|
+
return { content: [{ type: "text", text: JSON.stringify({ charged: true, data: result }, null, 2) }] };
|
|
39
|
+
} catch (err) {
|
|
40
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: err.message }) }] };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
server.tool(
|
|
46
|
+
"crm_marketplace_get_charges",
|
|
47
|
+
"List all marketplace charges for a location. Shows charge history, amounts, and status.",
|
|
48
|
+
{
|
|
49
|
+
location_id: z.string().describe("CRM location ID"),
|
|
50
|
+
},
|
|
51
|
+
async ({ location_id }) => {
|
|
52
|
+
try {
|
|
53
|
+
const sdk = await getSDK();
|
|
54
|
+
const result = await sdk.marketplace.getCharges({ locationId: location_id });
|
|
55
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
56
|
+
} catch (err) {
|
|
57
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: err.message }) }] };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
server.tool(
|
|
63
|
+
"crm_marketplace_has_funds",
|
|
64
|
+
"Check if a location has sufficient funds for a marketplace charge. Use before attempting to charge.",
|
|
65
|
+
{
|
|
66
|
+
location_id: z.string().describe("CRM location ID"),
|
|
67
|
+
amount: z.number().describe("Amount in cents to check"),
|
|
68
|
+
},
|
|
69
|
+
async ({ location_id, amount }) => {
|
|
70
|
+
try {
|
|
71
|
+
const sdk = await getSDK();
|
|
72
|
+
const result = await sdk.marketplace.hasFunds({ locationId: location_id, amount });
|
|
73
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
74
|
+
} catch (err) {
|
|
75
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: err.message }) }] };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
server.tool(
|
|
81
|
+
"crm_marketplace_installer_details",
|
|
82
|
+
"Get installer details for a marketplace app — which locations have it installed, installation status, and configuration.",
|
|
83
|
+
{
|
|
84
|
+
app_id: z.string().optional().describe("Marketplace app ID (default: 0nCORE app)"),
|
|
85
|
+
},
|
|
86
|
+
async ({ app_id }) => {
|
|
87
|
+
try {
|
|
88
|
+
const sdk = await getSDK();
|
|
89
|
+
const result = await sdk.marketplace.getInstallerDetails({
|
|
90
|
+
appId: app_id || "69c762225a31e1cd2f28dd4c",
|
|
91
|
+
});
|
|
92
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
93
|
+
} catch (err) {
|
|
94
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: err.message }) }] };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
server.tool(
|
|
100
|
+
"crm_marketplace_rebilling_config",
|
|
101
|
+
"Get the rebilling/subscription config for an app at a specific location. Shows pricing plans, usage limits, and subscription status.",
|
|
102
|
+
{
|
|
103
|
+
app_id: z.string().optional().describe("Marketplace app ID (default: 0nCORE app)"),
|
|
104
|
+
location_id: z.string().describe("CRM location ID"),
|
|
105
|
+
access_token: z.string().optional().describe("OAuth access token (auto-resolved)"),
|
|
106
|
+
},
|
|
107
|
+
async ({ app_id, location_id, access_token }) => {
|
|
108
|
+
try {
|
|
109
|
+
const appId = app_id || "69c762225a31e1cd2f28dd4c";
|
|
110
|
+
const token = access_token;
|
|
111
|
+
if (!token) {
|
|
112
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "access_token required for rebilling config" }) }] };
|
|
113
|
+
}
|
|
114
|
+
const headers = crmHeaders(token);
|
|
115
|
+
const res = await fetch(`${CRM_API_BASE}/marketplace/app/${appId}/rebilling-config/location/${location_id}`, { headers });
|
|
116
|
+
const data = await res.json();
|
|
117
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: res.ok, data }, null, 2) }] };
|
|
118
|
+
} catch (err) {
|
|
119
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: err.message }) }] };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
server.tool(
|
|
125
|
+
"crm_marketplace_delete_charge",
|
|
126
|
+
"Delete/cancel a specific marketplace charge.",
|
|
127
|
+
{
|
|
128
|
+
charge_id: z.string().describe("Charge ID to delete"),
|
|
129
|
+
location_id: z.string().describe("CRM location ID"),
|
|
130
|
+
},
|
|
131
|
+
async ({ charge_id, location_id }) => {
|
|
132
|
+
try {
|
|
133
|
+
const sdk = await getSDK();
|
|
134
|
+
const result = await sdk.marketplace.deleteCharge({ chargeId: charge_id, locationId: location_id });
|
|
135
|
+
return { content: [{ type: "text", text: JSON.stringify({ deleted: true, data: result }, null, 2) }] };
|
|
136
|
+
} catch (err) {
|
|
137
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: err.message }) }] };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
server.tool(
|
|
143
|
+
"crm_marketplace_uninstall",
|
|
144
|
+
"Uninstall a marketplace app from a location.",
|
|
145
|
+
{
|
|
146
|
+
app_id: z.string().optional().describe("Marketplace app ID"),
|
|
147
|
+
location_id: z.string().describe("CRM location ID"),
|
|
148
|
+
},
|
|
149
|
+
async ({ app_id, location_id }) => {
|
|
150
|
+
try {
|
|
151
|
+
const sdk = await getSDK();
|
|
152
|
+
const result = await sdk.marketplace.uninstallApplication({
|
|
153
|
+
appId: app_id || "69c762225a31e1cd2f28dd4c",
|
|
154
|
+
locationId: location_id,
|
|
155
|
+
});
|
|
156
|
+
return { content: [{ type: "text", text: JSON.stringify({ uninstalled: true, data: result }, null, 2) }] };
|
|
157
|
+
} catch (err) {
|
|
158
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: err.message }) }] };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
}
|