@indemn/cli 1.7.8 → 1.7.9
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.mcp.json +1 -1
- package/README.md +2 -2
- package/dist/mcp/tool-meta.d.ts.map +1 -1
- package/dist/mcp/tool-meta.js +47 -2
- package/dist/mcp/tool-meta.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +123 -4
- package/dist/mcp/tools.js.map +1 -1
- package/dist/sdk/campaigns.d.ts +82 -3
- package/dist/sdk/campaigns.d.ts.map +1 -1
- package/dist/sdk/campaigns.js +106 -4
- package/dist/sdk/campaigns.js.map +1 -1
- package/package.json +1 -1
- package/src/mcp/tool-meta.ts +49 -2
- package/src/mcp/tools.ts +162 -5
- package/src/sdk/campaigns.ts +125 -4
- package/test/mcp/__snapshots__/tools-snapshot.test.ts.snap +7 -0
- package/test/mcp/campaigns.test.ts +165 -0
- package/test/mcp/tools-snapshot.test.ts +2 -2
- package/test/sdk/campaigns.test.ts +76 -18
package/dist/sdk/campaigns.d.ts
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import type { IndemnClient } from './client.js';
|
|
2
|
+
export type CampaignStatus = 'draft' | 'running' | 'paused' | 'archived';
|
|
3
|
+
/** A touchpoint in a campaign's draft, found by its key. */
|
|
4
|
+
export type Touchpoint = {
|
|
5
|
+
key: string;
|
|
6
|
+
} & Record<string, unknown>;
|
|
7
|
+
type Draft = {
|
|
8
|
+
audience?: unknown;
|
|
9
|
+
touchpoints?: Touchpoint[];
|
|
10
|
+
escalation_policy?: unknown;
|
|
11
|
+
channel_policy?: unknown;
|
|
12
|
+
};
|
|
13
|
+
type Envelope = {
|
|
14
|
+
total?: number;
|
|
15
|
+
items?: Array<Record<string, unknown>>;
|
|
16
|
+
};
|
|
2
17
|
/**
|
|
3
|
-
* Campaigns
|
|
4
|
-
*
|
|
5
|
-
*
|
|
18
|
+
* Campaigns through copilot's proxy to the campaigns service: reads, and
|
|
19
|
+
* writes to configuration only (WRITABLE). The CLI never sends a
|
|
20
|
+
* communication, runs a book sync or deletes; those act on real customers.
|
|
6
21
|
*/
|
|
7
22
|
export declare class CampaignsSDK {
|
|
8
23
|
private client;
|
|
9
24
|
constructor(client: IndemnClient);
|
|
10
25
|
private read;
|
|
26
|
+
private write;
|
|
11
27
|
list(filter?: {
|
|
12
28
|
status?: string;
|
|
13
29
|
solutionId?: string;
|
|
@@ -31,5 +47,68 @@ export declare class CampaignsSDK {
|
|
|
31
47
|
/** A template; with versions: true, its versions (where subject and body live). */
|
|
32
48
|
template(id: string, versions?: boolean): Promise<unknown>;
|
|
33
49
|
templateSkills(solutionId?: string): Promise<unknown>;
|
|
50
|
+
templateSkill(id: string): Promise<{
|
|
51
|
+
name?: string;
|
|
52
|
+
instructions?: string;
|
|
53
|
+
}>;
|
|
54
|
+
books(filter?: {
|
|
55
|
+
solutionId?: string;
|
|
56
|
+
limit?: number;
|
|
57
|
+
}): Promise<Envelope>;
|
|
58
|
+
book(id: string): Promise<{
|
|
59
|
+
name?: string;
|
|
60
|
+
}>;
|
|
61
|
+
createCampaign(input: {
|
|
62
|
+
solution_id: string;
|
|
63
|
+
book_id: string;
|
|
64
|
+
name: string;
|
|
65
|
+
campaign_type: string;
|
|
66
|
+
entry?: string;
|
|
67
|
+
closes?: string;
|
|
68
|
+
}): Promise<{
|
|
69
|
+
id: string;
|
|
70
|
+
}>;
|
|
71
|
+
/** Change parts of the draft; the rest of it (its touchpoints among it) stays as it is. */
|
|
72
|
+
updateDraft(id: string, parts: Omit<Draft, 'touchpoints'>): Promise<Draft>;
|
|
73
|
+
/** Add a touchpoint, or change the one with its key (only the fields given). A new one drafts unless told to
|
|
74
|
+
* send; a template given without a version is pinned to its latest. */
|
|
75
|
+
setTouchpoint(id: string, touchpoint: Touchpoint): Promise<Draft>;
|
|
76
|
+
publish(id: string, changeSummary?: string): Promise<unknown>;
|
|
77
|
+
setStatus(id: string, status: CampaignStatus): Promise<unknown>;
|
|
78
|
+
createTemplate(input: Record<string, unknown> & {
|
|
79
|
+
uses_variables?: string[];
|
|
80
|
+
}): Promise<unknown>;
|
|
81
|
+
private latestTemplateVersion;
|
|
82
|
+
/** A new version of the template: its latest one with the changes given. */
|
|
83
|
+
updateTemplate(id: string, changes: Record<string, unknown> & {
|
|
84
|
+
uses_variables?: string[];
|
|
85
|
+
}): Promise<unknown>;
|
|
86
|
+
createTemplateSkill(input: {
|
|
87
|
+
solution_id?: string;
|
|
88
|
+
key: string;
|
|
89
|
+
name: string;
|
|
90
|
+
instructions: string;
|
|
91
|
+
}): Promise<unknown>;
|
|
92
|
+
updateTemplateSkill(id: string, changes: {
|
|
93
|
+
name?: string;
|
|
94
|
+
instructions?: string;
|
|
95
|
+
}): Promise<unknown>;
|
|
96
|
+
createBook(input: {
|
|
97
|
+
solution_id: string;
|
|
98
|
+
name: string;
|
|
99
|
+
schema_id?: string;
|
|
100
|
+
source?: unknown;
|
|
101
|
+
}): Promise<{
|
|
102
|
+
id: string;
|
|
103
|
+
}>;
|
|
104
|
+
/** Change the parts of a book that are given, each its own call in the service. Keys are the book's schema
|
|
105
|
+
* keys; identity keys change only while the book has no records. */
|
|
106
|
+
updateBook(id: string, changes: {
|
|
107
|
+
source?: unknown;
|
|
108
|
+
field_map?: unknown[];
|
|
109
|
+
identity_keys?: string[];
|
|
110
|
+
display?: unknown;
|
|
111
|
+
}): Promise<unknown>;
|
|
34
112
|
}
|
|
113
|
+
export {};
|
|
35
114
|
//# sourceMappingURL=campaigns.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"campaigns.d.ts","sourceRoot":"","sources":["../../src/sdk/campaigns.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"campaigns.d.ts","sourceRoot":"","sources":["../../src/sdk/campaigns.ts"],"names":[],"mappings":"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"}
|
package/dist/sdk/campaigns.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CliError, EXIT } from '../core/runtime.js';
|
|
2
|
-
/** Paths of the campaigns service the CLI may read. Everything else
|
|
2
|
+
/** Paths of the campaigns service the CLI may read. Everything else is refused. */
|
|
3
3
|
const READABLE = [
|
|
4
4
|
/^\/campaigns$/,
|
|
5
5
|
/^\/campaigns\/[\w-]+$/,
|
|
@@ -9,11 +9,34 @@ const READABLE = [
|
|
|
9
9
|
/^\/templates\/[\w-]+\/versions$/,
|
|
10
10
|
/^\/template-skills$/,
|
|
11
11
|
/^\/template-skills\/[\w-]+$/,
|
|
12
|
+
/^\/books$/,
|
|
13
|
+
/^\/books\/[\w-]+$/,
|
|
12
14
|
];
|
|
15
|
+
/** What the CLI may change: campaigns and their drafts (touchpoints live there), publish and status, templates
|
|
16
|
+
* (each change a new version), template skills and books. Never sending or resolving anything, book runs,
|
|
17
|
+
* uploads, test ticks or enrollments, and no deletes: those act on real customers or cannot be undone. */
|
|
18
|
+
const WRITABLE = [
|
|
19
|
+
['POST', /^\/campaigns$/],
|
|
20
|
+
['PUT', /^\/campaigns\/[\w-]+\/draft$/],
|
|
21
|
+
['POST', /^\/campaigns\/[\w-]+\/(publish|status)$/],
|
|
22
|
+
['POST', /^\/templates$/],
|
|
23
|
+
['POST', /^\/templates\/[\w-]+\/versions$/],
|
|
24
|
+
['POST', /^\/template-skills$/],
|
|
25
|
+
['PUT', /^\/template-skills\/[\w-]+$/],
|
|
26
|
+
['POST', /^\/books$/],
|
|
27
|
+
['PATCH', /^\/books\/[\w-]+\/source$/],
|
|
28
|
+
['PUT', /^\/books\/[\w-]+\/(field-map|identity-keys|display)$/],
|
|
29
|
+
];
|
|
30
|
+
// A template version is whole: a change starts from the latest version's content.
|
|
31
|
+
const TEMPLATE_CONTENT = ['subject', 'body', 'html', 'uses_variables', 'selection_tags', 'skills', 'batch_row'];
|
|
32
|
+
const given = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined));
|
|
33
|
+
// Templates declare the variables they read as {key, required, example}; given as keys, a key the template already
|
|
34
|
+
// declares keeps its entry.
|
|
35
|
+
const declare = (keys, current = []) => keys?.map((key) => current.find((v) => v.key === key) ?? { key });
|
|
13
36
|
/**
|
|
14
|
-
* Campaigns
|
|
15
|
-
*
|
|
16
|
-
*
|
|
37
|
+
* Campaigns through copilot's proxy to the campaigns service: reads, and
|
|
38
|
+
* writes to configuration only (WRITABLE). The CLI never sends a
|
|
39
|
+
* communication, runs a book sync or deletes; those act on real customers.
|
|
17
40
|
*/
|
|
18
41
|
export class CampaignsSDK {
|
|
19
42
|
client;
|
|
@@ -25,6 +48,11 @@ export class CampaignsSDK {
|
|
|
25
48
|
throw new CliError(`Campaigns path ${path} is not readable from the CLI.`, EXIT.NEEDS_INPUT);
|
|
26
49
|
return this.client.copilotGet(`/organization/:org_id/campaigns-service${path}`, query);
|
|
27
50
|
}
|
|
51
|
+
async write(method, path, body) {
|
|
52
|
+
if (!WRITABLE.some(([m, r]) => m === method && r.test(path)))
|
|
53
|
+
throw new CliError(`Campaigns ${method} ${path} is not allowed from the CLI.`, EXIT.NEEDS_INPUT);
|
|
54
|
+
return this.client.copilotRequestRaw(method, `/organization/:org_id/campaigns-service${path}`, body);
|
|
55
|
+
}
|
|
28
56
|
list(filter = {}) {
|
|
29
57
|
const q = { limit: String(Math.min(filter.limit ?? 50, 500)), skip: String(filter.skip ?? 0) };
|
|
30
58
|
if (filter.status)
|
|
@@ -55,5 +83,79 @@ export class CampaignsSDK {
|
|
|
55
83
|
templateSkills(solutionId) {
|
|
56
84
|
return this.read('/template-skills', solutionId ? { solution_id: solutionId } : undefined);
|
|
57
85
|
}
|
|
86
|
+
templateSkill(id) {
|
|
87
|
+
return this.read(`/template-skills/${id}`);
|
|
88
|
+
}
|
|
89
|
+
books(filter = {}) {
|
|
90
|
+
const q = { limit: String(Math.min(filter.limit ?? 50, 200)) }; // the service allows 200
|
|
91
|
+
if (filter.solutionId)
|
|
92
|
+
q.solution_id = filter.solutionId;
|
|
93
|
+
return this.read('/books', q);
|
|
94
|
+
}
|
|
95
|
+
book(id) {
|
|
96
|
+
return this.read(`/books/${id}`);
|
|
97
|
+
}
|
|
98
|
+
createCampaign(input) {
|
|
99
|
+
return this.write('POST', '/campaigns', given(input));
|
|
100
|
+
}
|
|
101
|
+
/** Change parts of the draft; the rest of it (its touchpoints among it) stays as it is. */
|
|
102
|
+
async updateDraft(id, parts) {
|
|
103
|
+
const { draft } = await this.read(`/campaigns/${id}`);
|
|
104
|
+
return this.write('PUT', `/campaigns/${id}/draft`, { ...draft, ...given(parts) });
|
|
105
|
+
}
|
|
106
|
+
/** Add a touchpoint, or change the one with its key (only the fields given). A new one drafts unless told to
|
|
107
|
+
* send; a template given without a version is pinned to its latest. */
|
|
108
|
+
async setTouchpoint(id, touchpoint) {
|
|
109
|
+
const { draft } = await this.read(`/campaigns/${id}`);
|
|
110
|
+
const current = draft?.touchpoints ?? [];
|
|
111
|
+
const existing = current.find((t) => t.key === touchpoint.key);
|
|
112
|
+
const tp = { ...(existing ?? { terminal_action: 'draft' }), ...given(touchpoint), key: touchpoint.key };
|
|
113
|
+
if (typeof touchpoint.template_id === 'string' && touchpoint.template_version_n === undefined) {
|
|
114
|
+
tp.template_version_n = Number((await this.latestTemplateVersion(touchpoint.template_id))?.version_n ?? 1);
|
|
115
|
+
}
|
|
116
|
+
const touchpoints = existing ? current.map((t) => (t.key === tp.key ? tp : t)) : [...current, tp];
|
|
117
|
+
return this.write('PUT', `/campaigns/${id}/draft`, { ...draft, touchpoints });
|
|
118
|
+
}
|
|
119
|
+
publish(id, changeSummary) {
|
|
120
|
+
return this.write('POST', `/campaigns/${id}/publish`, { change_summary: changeSummary ?? '' });
|
|
121
|
+
}
|
|
122
|
+
setStatus(id, status) {
|
|
123
|
+
return this.write('POST', `/campaigns/${id}/status`, { status });
|
|
124
|
+
}
|
|
125
|
+
createTemplate(input) {
|
|
126
|
+
return this.write('POST', '/templates', given({ ...input, uses_variables: declare(input.uses_variables) }));
|
|
127
|
+
}
|
|
128
|
+
async latestTemplateVersion(id) {
|
|
129
|
+
return (await this.read(`/templates/${id}/versions`)).items?.[0]; // newest first
|
|
130
|
+
}
|
|
131
|
+
/** A new version of the template: its latest one with the changes given. */
|
|
132
|
+
async updateTemplate(id, changes) {
|
|
133
|
+
const latest = (await this.latestTemplateVersion(id)) ?? {};
|
|
134
|
+
const kept = Object.fromEntries(TEMPLATE_CONTENT.filter((k) => k in latest).map((k) => [k, latest[k]]));
|
|
135
|
+
return this.write('POST', `/templates/${id}/versions`, { ...kept, ...given({ ...changes, uses_variables: declare(changes.uses_variables, latest.uses_variables) }) });
|
|
136
|
+
}
|
|
137
|
+
createTemplateSkill(input) {
|
|
138
|
+
return this.write('POST', '/template-skills', input);
|
|
139
|
+
}
|
|
140
|
+
async updateTemplateSkill(id, changes) {
|
|
141
|
+
const skill = await this.templateSkill(id);
|
|
142
|
+
return this.write('PUT', `/template-skills/${id}`, { name: skill.name, instructions: skill.instructions, ...given(changes) });
|
|
143
|
+
}
|
|
144
|
+
createBook(input) {
|
|
145
|
+
return this.write('POST', '/books', given(input));
|
|
146
|
+
}
|
|
147
|
+
/** Change the parts of a book that are given, each its own call in the service. Keys are the book's schema
|
|
148
|
+
* keys; identity keys change only while the book has no records. */
|
|
149
|
+
async updateBook(id, changes) {
|
|
150
|
+
if (changes.source !== undefined)
|
|
151
|
+
await this.write('PATCH', `/books/${id}/source`, { source: changes.source });
|
|
152
|
+
if (changes.field_map !== undefined)
|
|
153
|
+
await this.write('PUT', `/books/${id}/field-map`, { field_map: changes.field_map });
|
|
154
|
+
if (changes.identity_keys !== undefined)
|
|
155
|
+
await this.write('PUT', `/books/${id}/identity-keys`, { identity_keys: changes.identity_keys });
|
|
156
|
+
if (changes.display !== undefined)
|
|
157
|
+
await this.write('PUT', `/books/${id}/display`, { display: changes.display });
|
|
158
|
+
return this.book(id);
|
|
159
|
+
}
|
|
58
160
|
}
|
|
59
161
|
//# sourceMappingURL=campaigns.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"campaigns.js","sourceRoot":"","sources":["../../src/sdk/campaigns.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAEpD,
|
|
1
|
+
{"version":3,"file":"campaigns.js","sourceRoot":"","sources":["../../src/sdk/campaigns.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/mcp/tool-meta.ts
CHANGED
|
@@ -29,6 +29,17 @@ const write = (title: string, extra: Partial<ToolAnnotations> = {}): ToolMeta =>
|
|
|
29
29
|
annotations: { title, readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false, ...extra },
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
/** The parts a call changes, in words: "audience, channel policy". */
|
|
33
|
+
const parts = (a: Record<string, unknown>, keys: string[]) => keys.filter((k) => a[k] !== undefined && a[k] !== '').map((k) => k.replace(/_/g, ' ')).join(', ');
|
|
34
|
+
|
|
35
|
+
/** A book change with its new values, so the person sees what they approve: "field map to Company → account.name". */
|
|
36
|
+
const bookChanges = (a: Record<string, any>) => [
|
|
37
|
+
a.source !== undefined && `source to ${a.source?.kind ?? JSON.stringify(a.source)}`,
|
|
38
|
+
Array.isArray(a.field_map) && `field map to ${a.field_map.map((m: { source_path?: string; schema_key?: string }) => `${m.source_path} → ${m.schema_key}`).join(', ')}`,
|
|
39
|
+
Array.isArray(a.identity_keys) && `identity keys to ${a.identity_keys.join(', ')}`,
|
|
40
|
+
a.display && `display to ${['primary', 'secondary'].filter((k) => Array.isArray(a.display[k])).map((k) => `${k} ${a.display[k].join(', ')}`).join('; ')}`,
|
|
41
|
+
].filter(Boolean).join('; ');
|
|
42
|
+
|
|
32
43
|
/** Overwrites or removes something that cannot be restored from the tool itself. */
|
|
33
44
|
const destroy = (title: string, extra: Partial<ToolAnnotations> = {}): ToolMeta => ({
|
|
34
45
|
title,
|
|
@@ -217,8 +228,44 @@ export const TOOL_META: Record<string, ToolMeta> = {
|
|
|
217
228
|
// Billing (read-only)
|
|
218
229
|
get_billing: read('Billing status, invoices, usage or plans'),
|
|
219
230
|
|
|
220
|
-
// Campaigns
|
|
221
|
-
get_campaigns: read('Read campaigns and
|
|
231
|
+
// Campaigns: each change is approved on a card; nothing here sends a message, and nothing deletes.
|
|
232
|
+
get_campaigns: read('Read campaigns, templates and books'),
|
|
233
|
+
save_campaign: {
|
|
234
|
+
...write('Create or change campaign'), guard: true,
|
|
235
|
+
describe: (a) => a.campaign_id
|
|
236
|
+
? `This will change the ${parts(a, ['audience', 'escalation_policy', 'channel_policy'])} in the draft of campaign ${a.campaign_id} (it takes effect when the campaign is published).`
|
|
237
|
+
: `This will create campaign "${a.name}" as a draft; it sends nothing until it is published and started.`,
|
|
238
|
+
},
|
|
239
|
+
set_touchpoint: {
|
|
240
|
+
...write('Add or change touchpoint', { idempotentHint: true }), guard: true,
|
|
241
|
+
describe: (a) => `This will add or change touchpoint "${a.key}" in the draft of campaign ${a.campaign_id}${a.channel ? ` (${a.channel})` : ''}${a.terminal_action === 'send' ? '. It is set to SEND to customers directly once the campaign is published and running' : ''}.`,
|
|
242
|
+
},
|
|
243
|
+
publish_campaign: { ...write('Publish campaign'), guard: true, describe: (a) => `This will publish the draft of campaign ${a.campaign_id} as its next version; if it is running, it uses it from its next run.` },
|
|
244
|
+
set_campaign_status: {
|
|
245
|
+
...write('Start, pause or archive campaign'), guard: true,
|
|
246
|
+
describe: (a) => a.status === 'running'
|
|
247
|
+
? `This will START campaign ${a.campaign_id}: it enrols the matching records of its book and runs its touchpoints (touchpoints set to send reach real customers).`
|
|
248
|
+
: a.status === 'archived' ? `This will archive campaign ${a.campaign_id} for good: it cannot be started again.`
|
|
249
|
+
: `This will pause campaign ${a.campaign_id}: its touchpoints stop until it is started again.`,
|
|
250
|
+
},
|
|
251
|
+
save_campaign_template: {
|
|
252
|
+
...write('Create or change campaign template'), guard: true,
|
|
253
|
+
describe: (a) => a.template_id
|
|
254
|
+
? `This will save a new version of template ${a.template_id} (${parts(a, ['subject', 'body', 'html', 'uses_variables', 'skills', 'selection_tags'])}); touchpoints keep the version they are set to until it is set again.`
|
|
255
|
+
: `This will create ${a.channel} template "${a.name}".`,
|
|
256
|
+
},
|
|
257
|
+
save_template_skill: {
|
|
258
|
+
...write('Create or change template skill'), guard: true,
|
|
259
|
+
describe: (a) => a.template_skill_id
|
|
260
|
+
? `This will change template skill ${a.template_skill_id} (${parts(a, ['name', 'instructions'])}); templates using it write with it from their campaigns' next run.`
|
|
261
|
+
: `This will create template skill "${a.name}".`,
|
|
262
|
+
},
|
|
263
|
+
save_book: {
|
|
264
|
+
...write('Create or change book'), guard: true,
|
|
265
|
+
describe: (a) => a.book_id
|
|
266
|
+
? `This will change book ${a.book_id}: ${bookChanges(a)}.`
|
|
267
|
+
: `This will create book "${a.name}" for this solution.`,
|
|
268
|
+
},
|
|
222
269
|
|
|
223
270
|
// V2 platform and Jarvis (Darwin)
|
|
224
271
|
platform_list: read('List V2 platform items'),
|
package/src/mcp/tools.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { DistributionSDK } from '../sdk/distribution.js';
|
|
|
13
13
|
import { AnalyticsSDK, AuditSDK, OrgSettingsSDK } from '../sdk/analytics.js';
|
|
14
14
|
import { TicketsSDK } from '../sdk/tickets.js';
|
|
15
15
|
import { BillingSDK } from '../sdk/billing.js';
|
|
16
|
-
import { CampaignsSDK } from '../sdk/campaigns.js';
|
|
16
|
+
import { CampaignsSDK, type Touchpoint } from '../sdk/campaigns.js';
|
|
17
17
|
import { PlatformSDK } from '../sdk/platform.js';
|
|
18
18
|
import { jarvisChat } from '../sdk/jarvis-chat.js';
|
|
19
19
|
import { applyConfigChange, describePlan, liveText, mayBeLive, modelArg, planConfigChange, type ConfigChange } from '../sdk/config-change.js';
|
|
@@ -168,6 +168,11 @@ const skillDefinition = z.preprocess(parseJsonText, z.object({
|
|
|
168
168
|
}).passthrough()).optional(),
|
|
169
169
|
}).passthrough());
|
|
170
170
|
|
|
171
|
+
// Campaigns are on for an org only when Indemn has turned Solutions on: the dashboard hides them otherwise, and the
|
|
172
|
+
// campaigns service does not check it, so these tools ask first (before any approval card).
|
|
173
|
+
const CAMPAIGN_TOOLS = new Set(['get_campaigns', 'save_campaign', 'set_touchpoint', 'publish_campaign', 'set_campaign_status', 'save_campaign_template', 'save_template_skill', 'save_book']);
|
|
174
|
+
const SOLUTIONS_OFF = 'Campaigns are off for this organization: Solutions is not turned on (the dashboard hides campaigns too). Indemn staff can turn it on with set_org_admin_settings (solutions: true); anyone else asks the Indemn team to turn it on.';
|
|
175
|
+
|
|
171
176
|
export function registerTools(server: McpServer, client: IndemnClient): Registry {
|
|
172
177
|
// Role awareness: the folder's linked org (or INDEMN_ORG_ID) narrows what counts; otherwise any org.
|
|
173
178
|
const orgInView = () => process.env.INDEMN_ORG_ID || client.getLink()?.org_id || undefined;
|
|
@@ -177,6 +182,10 @@ export function registerTools(server: McpServer, client: IndemnClient): Registry
|
|
|
177
182
|
visible: (name) => usableSomewhere(name, currentAccess(client), orgInView()),
|
|
178
183
|
// Refuse early only when the saved access still says no after asking again (it may be stale).
|
|
179
184
|
deny: async (name, orgId) => {
|
|
185
|
+
if (CAMPAIGN_TOOLS.has(name) && orgId) {
|
|
186
|
+
const on = await client.copilotGet<{ enableSolutions?: boolean }>(`/organization/${orgId}/configurations`).then((c) => c?.enableSolutions === true, () => false);
|
|
187
|
+
if (!on) return SOLUTIONS_OFF;
|
|
188
|
+
}
|
|
180
189
|
if (!orgDenial(name, currentAccess(client), orgId)) return null;
|
|
181
190
|
return orgDenial(name, await refreshAccess(client), orgId);
|
|
182
191
|
},
|
|
@@ -219,7 +228,12 @@ export function registerTools(server: McpServer, client: IndemnClient): Registry
|
|
|
219
228
|
add(args.ticket_id, null, (id) => tickets.get(id).then((t) => { const x = t as { ticket_ref?: string; title?: string }; return label([x.ticket_ref, x.title].filter(Boolean).join(' ')); })),
|
|
220
229
|
add(args.rule_id, null, (id) => tickets.listRules({ includeTrashed: true }).then((rules) => label((rules as Array<{ _id?: string; id?: string; name?: string }>).find((r) => (r._id ?? r.id) === id)?.name))),
|
|
221
230
|
add(args.distribution_id, null, (id) => distribution.list().then((d) => { const p = (d.paths ?? []).find((x) => x.id === id); return label(p ? `${p.path_name ?? ''} (${p.host_name ?? '*'}${p.path_expression ?? ''})`.trim() : undefined); })),
|
|
222
|
-
|
|
231
|
+
// A website template, else a campaign template.
|
|
232
|
+
add(args.template_id, null, (id) => distribution.listTemplates().then((ts) => label((ts as Array<{ _id?: string; name?: string }>).find((t) => t._id === id)?.name))
|
|
233
|
+
.then((l) => l.name ? l : new CampaignsSDK(client).template(id).then((t) => label((t as { name?: unknown }).name), () => l))),
|
|
234
|
+
add(args.campaign_id, 'campaign', (id) => new CampaignsSDK(client).get(id) as Promise<{ name?: unknown }>),
|
|
235
|
+
add(args.book_id, 'book', (id) => new CampaignsSDK(client).book(id)),
|
|
236
|
+
add(args.template_skill_id, 'template skill', (id) => new CampaignsSDK(client).templateSkill(id)),
|
|
223
237
|
add(args.owner_id, null, (id) => orgs.listMembers().then((ms) => { const m = ms.find((x) => x.user_id === id); return label(m?.name ?? m?.email); })),
|
|
224
238
|
agentId && typeof args.function_id === 'string' ? add(args.parameter_id, null, (id) => skillOf(agentId, String(args.function_id)).then((s) => functions.listParameters(agentId, s._id)).then((ps) => label(ps.find((p) => p._id === id)?.name))) : undefined,
|
|
225
239
|
add(orgId, null, (id) => client.copilotGet<{ organization?: { name?: string } }>(`/organization/${id}`).then((r) => r.organization)),
|
|
@@ -1860,10 +1874,10 @@ export function registerTools(server: McpServer, client: IndemnClient): Registry
|
|
|
1860
1874
|
|
|
1861
1875
|
reg.tool(
|
|
1862
1876
|
'get_campaigns',
|
|
1863
|
-
'Read
|
|
1877
|
+
'Read campaigns: list, one campaign (campaign_id, with its draft and touchpoints), its counts / forecast / next-touchpoints / versions, templates, one template (template_id, versions: true for subject and body), template skills, books, or one book (book_id).',
|
|
1864
1878
|
{
|
|
1865
|
-
what: z.enum(['list', 'campaign', 'counts', 'forecast', 'next-touchpoints', 'versions', 'templates', 'template', 'template-skills']),
|
|
1866
|
-
campaign_id: z.string().optional(), template_id: z.string().optional(), versions: z.boolean().optional(),
|
|
1879
|
+
what: z.enum(['list', 'campaign', 'counts', 'forecast', 'next-touchpoints', 'versions', 'templates', 'template', 'template-skills', 'books', 'book']),
|
|
1880
|
+
campaign_id: z.string().optional(), template_id: z.string().optional(), book_id: z.string().optional(), versions: z.boolean().optional(),
|
|
1867
1881
|
status: z.string().optional(), solution_id: z.string().optional(), channel: z.string().optional(),
|
|
1868
1882
|
days: z.number().int().min(1).max(365).optional(), limit: z.number().int().min(1).max(500).optional(),
|
|
1869
1883
|
},
|
|
@@ -1877,12 +1891,155 @@ export function registerTools(server: McpServer, client: IndemnClient): Registry
|
|
|
1877
1891
|
case 'templates': return success(await c.templates({ channel: args.channel, solutionId: args.solution_id, limit: args.limit }));
|
|
1878
1892
|
case 'template': return success(await c.template(need(args.template_id, 'template_id'), Boolean(args.versions)));
|
|
1879
1893
|
case 'template-skills': return success(await c.templateSkills(args.solution_id));
|
|
1894
|
+
case 'books': return success(await c.books({ solutionId: args.solution_id, limit: args.limit }));
|
|
1895
|
+
case 'book': return success(await c.book(need(args.book_id, 'book_id')));
|
|
1880
1896
|
default: return success(await c.detail(need(args.campaign_id, 'campaign_id'), args.what, args.days));
|
|
1881
1897
|
}
|
|
1882
1898
|
} catch (err) { return error(err); }
|
|
1883
1899
|
},
|
|
1884
1900
|
);
|
|
1885
1901
|
|
|
1902
|
+
// Campaigns configuration: every change is approved on a card. Nothing here sends a message, runs a book sync or
|
|
1903
|
+
// deletes (CampaignsSDK WRITABLE); a campaign ends archived. A save_* tool given an id changes that item, else creates.
|
|
1904
|
+
const campaigns = new CampaignsSDK(client);
|
|
1905
|
+
type CampaignArgs = Record<string, any>;
|
|
1906
|
+
// Only the tool's own arguments reach the service: not confirm/org_id, and not the "" or null a model fills unused
|
|
1907
|
+
// fields with (an empty value never blanks a saved field).
|
|
1908
|
+
const own = (args: CampaignArgs): CampaignArgs => Object.fromEntries(Object.entries(args).filter(([k, v]) => k !== 'confirm' && k !== 'org_id' && v !== '' && v !== null));
|
|
1909
|
+
const lacking = (a: CampaignArgs, keys: string[]) => keys.filter((k) => a[k] === undefined);
|
|
1910
|
+
// `problem` is checked before the card: a call that cannot work is answered at once, without asking or sending.
|
|
1911
|
+
const campaignTool = (name: string, description: string, shape: Record<string, z.ZodTypeAny>, run: (a: CampaignArgs) => Promise<unknown>, problem: (a: CampaignArgs) => string | null = () => null) =>
|
|
1912
|
+
reg.tool(name, description, shape, async (args) => {
|
|
1913
|
+
try {
|
|
1914
|
+
const a = own(args);
|
|
1915
|
+
const p = problem(a);
|
|
1916
|
+
if (p) throw new Error(p);
|
|
1917
|
+
return success(await run(a));
|
|
1918
|
+
} catch (err) { return error(err); }
|
|
1919
|
+
}, { guardWhen: async (args) => problem(own(args)) === null });
|
|
1920
|
+
// A create and then a change: if the change is refused, the item exists, so it is changed with its id next time.
|
|
1921
|
+
const thenChange = async (what: string, made: { id: string }, change: () => Promise<unknown>) => {
|
|
1922
|
+
try { return await change(); } catch (err) { throw new Error(`The ${what} was created (id ${made.id}), but ${err instanceof Error ? err.message : String(err)}. Change it with its id; do not create it again.`); }
|
|
1923
|
+
};
|
|
1924
|
+
const rule = (what: string) => jsonObject().describe(`${what}: a rule as an object or JSON text, e.g. {"all":[{"field":"status","op":"eq","value":"active"},{"field":"derived.days_out","op":"lte","value":60}]}. Ops: eq ne lt lte gt gte in nin exists missing contains; all / any / not combine rules.`);
|
|
1925
|
+
const DRAFT_PARTS = ['audience', 'escalation_policy', 'channel_policy'];
|
|
1926
|
+
|
|
1927
|
+
campaignTool(
|
|
1928
|
+
'save_campaign',
|
|
1929
|
+
"Create a campaign (no campaign_id) as a draft for a solution's book, or change a campaign's draft: its audience (which records belong) and escalation and channel policies; what is not given stays. Touchpoints are set with set_touchpoint. Nothing is sent until the campaign is published (publish_campaign) and started (set_campaign_status running). Find the solution with list_solutions and its book with get_campaigns (what: books).",
|
|
1930
|
+
{
|
|
1931
|
+
campaign_id: z.string().optional().describe('The campaign to change; leave out to create one'),
|
|
1932
|
+
solution_id: z.string().optional(), book_id: z.string().optional(), name: z.string().optional(),
|
|
1933
|
+
campaign_type: z.string().optional().describe('To create: e.g. renewal, outreach'),
|
|
1934
|
+
entry: z.enum(['rolling', 'cohort']).optional().describe('To create: rolling (records join as they match, the default) or cohort (a fixed set, with closes)'),
|
|
1935
|
+
closes: z.string().optional().describe('To create a cohort campaign: ISO date it closes'),
|
|
1936
|
+
audience: rule('Which records belong').optional(),
|
|
1937
|
+
escalation_policy: jsonObject().optional().describe('e.g. {"after":{"touches_exhausted":true},"on_hard_failure":"escalate_immediately","then":"create_work_item","assign_to":null}'),
|
|
1938
|
+
channel_policy: jsonObject().optional().describe('e.g. {"quiet_hours":null,"max_per_record_per_window":null}'),
|
|
1939
|
+
},
|
|
1940
|
+
async ({ campaign_id, audience, escalation_policy, channel_policy, ...create }) => {
|
|
1941
|
+
const parts = { audience, escalation_policy, channel_policy };
|
|
1942
|
+
if (campaign_id) return campaigns.updateDraft(campaign_id, parts);
|
|
1943
|
+
const made = await campaigns.createCampaign(create as Parameters<CampaignsSDK['createCampaign']>[0]);
|
|
1944
|
+
return DRAFT_PARTS.some((k) => (parts as CampaignArgs)[k] !== undefined) ? thenChange('campaign', made, async () => ({ ...made, draft: await campaigns.updateDraft(made.id, parts) })) : made;
|
|
1945
|
+
},
|
|
1946
|
+
(a) => a.campaign_id
|
|
1947
|
+
? (DRAFT_PARTS.some((k) => a[k] !== undefined) ? null : 'Nothing to change: give audience, escalation_policy or channel_policy (a campaign is not renamed here).')
|
|
1948
|
+
: (lacking(a, ['solution_id', 'book_id', 'name', 'campaign_type']).length ? `To create a campaign also give ${lacking(a, ['solution_id', 'book_id', 'name', 'campaign_type']).join(', ')}; to change one, give its campaign_id.` : null),
|
|
1949
|
+
);
|
|
1950
|
+
|
|
1951
|
+
campaignTool(
|
|
1952
|
+
'set_touchpoint',
|
|
1953
|
+
"Add a touchpoint to a campaign's draft, or change the one with the same key (only the fields given change). when is a trigger rule: days before the due date ({\"field\":\"derived.days_out\",\"op\":\"lte\",\"value\":45}), a day of the month (calendar.day_of_month), days without a reply (silence.days) or an inbound intent (intent). terminal_action draft (the default) prepares the message for a person to review; send sends it to the customer, so use send only when the person asks for it. A template given without a version is pinned to its latest.",
|
|
1954
|
+
{
|
|
1955
|
+
campaign_id: z.string(),
|
|
1956
|
+
key: z.string().trim().min(1).describe('Unique in the campaign, e.g. t2_reminder_45'),
|
|
1957
|
+
when: rule('When it fires').optional(),
|
|
1958
|
+
channel: z.enum(['email', 'voice', 'sms']).optional(),
|
|
1959
|
+
terminal_action: z.enum(['draft', 'send']).optional(),
|
|
1960
|
+
order: z.number().int().optional(),
|
|
1961
|
+
template_id: z.string().optional(), template_version_n: z.number().int().optional(),
|
|
1962
|
+
grouping: jsonObject().optional().describe('e.g. {"key":"insured_account","recipient":"resolved"} (key: record, insured_account or fixed_recipient)'),
|
|
1963
|
+
required_variables: jsonArray(z.string()).optional(),
|
|
1964
|
+
},
|
|
1965
|
+
async ({ campaign_id, ...touchpoint }) => campaigns.setTouchpoint(campaign_id, touchpoint as Touchpoint),
|
|
1966
|
+
);
|
|
1967
|
+
|
|
1968
|
+
campaignTool(
|
|
1969
|
+
'publish_campaign',
|
|
1970
|
+
"Publish a campaign's draft as its next version (refused, with every problem listed, while it has no audience or touchpoint). A running campaign uses the new version from its next run.",
|
|
1971
|
+
{ campaign_id: z.string(), change_summary: z.string().optional() },
|
|
1972
|
+
async (a) => campaigns.publish(a.campaign_id, a.change_summary),
|
|
1973
|
+
);
|
|
1974
|
+
|
|
1975
|
+
campaignTool(
|
|
1976
|
+
'set_campaign_status',
|
|
1977
|
+
'Start (running), pause (paused) or archive (archived, for good) a campaign. Starting needs a published version; it then enrols the matching records of its book and runs its touchpoints. Campaigns are never deleted: asked to delete one, offer to archive it and say it is archived, not deleted.',
|
|
1978
|
+
{ campaign_id: z.string(), status: z.enum(['running', 'paused', 'archived']) },
|
|
1979
|
+
async (a) => campaigns.setStatus(a.campaign_id, a.status),
|
|
1980
|
+
);
|
|
1981
|
+
|
|
1982
|
+
const TEMPLATE_PARTS = ['subject', 'body', 'html', 'uses_variables', 'skills', 'selection_tags'];
|
|
1983
|
+
campaignTool(
|
|
1984
|
+
'save_campaign_template',
|
|
1985
|
+
'Create a campaign message template (no template_id), or change one: a change is saved as its new version, made from the latest one with the fields given. Touchpoints keep the version they are set to until set_touchpoint sets the template again.',
|
|
1986
|
+
{
|
|
1987
|
+
template_id: z.string().optional().describe('The template to change; leave out to create one'),
|
|
1988
|
+
solution_id: z.string().optional(), name: z.string().optional(), channel: z.enum(['email', 'voice', 'sms']).optional(),
|
|
1989
|
+
subject: z.string().optional().describe('Email subject. Placeholders: {{ record.<book field> }}, e.g. {{ record.policy.number }}, or contact.*, campaign.*, enrollment.*'),
|
|
1990
|
+
body: z.string().optional().describe('Message text, with placeholders as in subject'),
|
|
1991
|
+
html: z.string().optional(),
|
|
1992
|
+
uses_variables: jsonArray(z.string()).optional().describe('Every placeholder key the template uses, e.g. ["record.policy.number","contact.first_name"] (the service refuses an undeclared one)'),
|
|
1993
|
+
skills: jsonObject().optional(), selection_tags: jsonObject().optional(),
|
|
1994
|
+
change_summary: z.string().optional(),
|
|
1995
|
+
},
|
|
1996
|
+
async ({ template_id, solution_id, name, channel, ...content }) => template_id
|
|
1997
|
+
? campaigns.updateTemplate(template_id, content)
|
|
1998
|
+
: campaigns.createTemplate({ solution_id, name, channel, ...content }),
|
|
1999
|
+
(a) => a.template_id
|
|
2000
|
+
? (TEMPLATE_PARTS.some((k) => a[k] !== undefined) ? null : `Nothing to change: give ${TEMPLATE_PARTS.join(', ')}.`)
|
|
2001
|
+
: (lacking(a, ['solution_id', 'name', 'channel', 'body']).length ? `To create a template also give ${lacking(a, ['solution_id', 'name', 'channel', 'body']).join(', ')}; to change one, give its template_id.` : null),
|
|
2002
|
+
);
|
|
2003
|
+
|
|
2004
|
+
campaignTool(
|
|
2005
|
+
'save_template_skill',
|
|
2006
|
+
"Create a template skill (no template_skill_id): writing instructions a template uses when its message is composed. Or change one's name or instructions; templates using it write with the new text from their campaigns' next run, with no publish.",
|
|
2007
|
+
{
|
|
2008
|
+
template_skill_id: z.string().optional().describe('The skill to change; leave out to create one'),
|
|
2009
|
+
solution_id: z.string().optional(), key: z.string().optional().describe('To create: unique in the solution, e.g. friendly_tone'),
|
|
2010
|
+
name: z.string().optional(), instructions: z.string().optional(),
|
|
2011
|
+
},
|
|
2012
|
+
async ({ template_skill_id, solution_id, key, name, instructions }) => template_skill_id
|
|
2013
|
+
? campaigns.updateTemplateSkill(template_skill_id, { name, instructions })
|
|
2014
|
+
: campaigns.createTemplateSkill({ solution_id, key, name, instructions }),
|
|
2015
|
+
(a) => a.template_skill_id
|
|
2016
|
+
? (a.name !== undefined || a.instructions !== undefined ? null : 'Nothing to change: give name or instructions.')
|
|
2017
|
+
: (lacking(a, ['solution_id', 'key', 'name', 'instructions']).length ? `To create a template skill also give ${lacking(a, ['solution_id', 'key', 'name', 'instructions']).join(', ')}; to change one, give its template_skill_id.` : null),
|
|
2018
|
+
);
|
|
2019
|
+
|
|
2020
|
+
const BOOK_PARTS = ['source', 'field_map', 'identity_keys', 'display'];
|
|
2021
|
+
campaignTool(
|
|
2022
|
+
'save_book',
|
|
2023
|
+
"Create a solution's book, the records a campaign works from (no book_id; a solution has one book, so look with get_campaigns what: books first). Or change a book's source, field map (which source column fills which field), identity keys (what makes a record unique; only while it has no records) or display (fields shown for a record); each part given replaces the book's. Keys are the book's schema keys, e.g. policy.number, insured.name.",
|
|
2024
|
+
{
|
|
2025
|
+
book_id: z.string().optional().describe('The book to change; leave out to create one'),
|
|
2026
|
+
solution_id: z.string().optional(), name: z.string().optional(),
|
|
2027
|
+
schema_id: z.string().optional().describe("To create: the entity schema; left out, the solution type's default fields"),
|
|
2028
|
+
source: jsonObject().optional().describe('{"kind":"upload"} (records uploaded as CSV in the dashboard) or {"kind":"connection","integration_id":"…"}'),
|
|
2029
|
+
field_map: jsonArray(jsonObject()).optional().describe('e.g. [{"schema_key":"policy.number","source_path":"Policy #"}]'),
|
|
2030
|
+
identity_keys: jsonArray(z.string()).optional().describe('e.g. ["policy.number"]'),
|
|
2031
|
+
display: jsonObject().optional().describe('e.g. {"primary":["policy.number"],"secondary":["insured.name"]}'),
|
|
2032
|
+
},
|
|
2033
|
+
async ({ book_id, solution_id, name, schema_id, source, ...parts }) => {
|
|
2034
|
+
if (book_id) return campaigns.updateBook(book_id, { source, ...parts });
|
|
2035
|
+
const made = await campaigns.createBook({ solution_id, name, schema_id, source });
|
|
2036
|
+
return Object.keys(parts).length ? thenChange('book', made, () => campaigns.updateBook(made.id, parts)) : made;
|
|
2037
|
+
},
|
|
2038
|
+
(a) => a.book_id
|
|
2039
|
+
? (BOOK_PARTS.some((k) => a[k] !== undefined) ? null : `Nothing to change: give ${BOOK_PARTS.join(', ')}.`)
|
|
2040
|
+
: (lacking(a, ['solution_id', 'name']).length ? `To create a book also give ${lacking(a, ['solution_id', 'name']).join(', ')}; to change one, give its book_id.` : null),
|
|
2041
|
+
);
|
|
2042
|
+
|
|
1886
2043
|
const platform = new PlatformSDK(client);
|
|
1887
2044
|
const platformResource = z.enum(['agents', 'templates', 'components', 'connectors', 'connector-bindings', 'scenarios']);
|
|
1888
2045
|
|