@sentientui/mcp 0.8.0 → 0.8.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/dist/{chunk-E34O3H2Z.js → chunk-TLBLOBSB.js} +244 -201
- package/dist/index.cjs +248 -204
- package/dist/index.js +7 -5
- package/dist/lib.cjs +242 -202
- package/dist/lib.js +1 -1
- package/package.json +10 -9
package/dist/index.cjs
CHANGED
|
@@ -15,16 +15,15 @@ var ApiError = class extends Error {
|
|
|
15
15
|
this.status = status;
|
|
16
16
|
this.name = "ApiError";
|
|
17
17
|
}
|
|
18
|
-
status;
|
|
19
18
|
};
|
|
20
19
|
var ApiClient = class {
|
|
21
|
-
baseUrl;
|
|
22
|
-
apiKey;
|
|
23
20
|
constructor(opts) {
|
|
21
|
+
var _a2;
|
|
24
22
|
this.apiKey = opts.apiKey;
|
|
25
|
-
this.baseUrl = (opts.baseUrl
|
|
23
|
+
this.baseUrl = ((_a2 = opts.baseUrl) != null ? _a2 : "https://api.sentient-ui.com").replace(/\/$/, "");
|
|
26
24
|
}
|
|
27
25
|
async get(path) {
|
|
26
|
+
var _a2;
|
|
28
27
|
const res = await fetch(`${this.baseUrl}/v1/mgmt${path}`, {
|
|
29
28
|
headers: {
|
|
30
29
|
authorization: `Bearer ${this.apiKey}`,
|
|
@@ -33,11 +32,12 @@ var ApiClient = class {
|
|
|
33
32
|
});
|
|
34
33
|
if (!res.ok) {
|
|
35
34
|
const body = await res.json().catch(() => ({}));
|
|
36
|
-
throw new ApiError(res.status, String(body.error
|
|
35
|
+
throw new ApiError(res.status, String((_a2 = body.error) != null ? _a2 : res.statusText));
|
|
37
36
|
}
|
|
38
37
|
return res.json();
|
|
39
38
|
}
|
|
40
39
|
async post(path, body) {
|
|
40
|
+
var _a2;
|
|
41
41
|
const res = await fetch(`${this.baseUrl}/v1/mgmt${path}`, {
|
|
42
42
|
method: "POST",
|
|
43
43
|
headers: {
|
|
@@ -48,7 +48,7 @@ var ApiClient = class {
|
|
|
48
48
|
});
|
|
49
49
|
if (!res.ok) {
|
|
50
50
|
const errBody = await res.json().catch(() => ({}));
|
|
51
|
-
throw new ApiError(res.status, String(errBody.error
|
|
51
|
+
throw new ApiError(res.status, String((_a2 = errBody.error) != null ? _a2 : res.statusText));
|
|
52
52
|
}
|
|
53
53
|
return res.json();
|
|
54
54
|
}
|
|
@@ -59,8 +59,47 @@ var import_node_module = require("module");
|
|
|
59
59
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
60
60
|
|
|
61
61
|
// src/tools/projects.ts
|
|
62
|
+
var import_zod2 = require("zod");
|
|
63
|
+
|
|
64
|
+
// src/tools/common.ts
|
|
62
65
|
var import_zod = require("zod");
|
|
63
66
|
var projectIdSchema = import_zod.z.string().uuid().describe("The project UUID");
|
|
67
|
+
function apiErrorGuidance(err) {
|
|
68
|
+
switch (err.message) {
|
|
69
|
+
case "insufficient_scope":
|
|
70
|
+
return "This action needs an account login. Connect via the hosted MCP URL (https://api.sentient-ui.com/mcp) and sign in \u2014 a project-scoped server key (sk_\u2026) or anonymous demo token cannot do this.";
|
|
71
|
+
case "demo_read_only":
|
|
72
|
+
return "Demo mode is read-only. Create a SentientUI account and sign in (or use a project server key) to make changes.";
|
|
73
|
+
case "insufficient_role":
|
|
74
|
+
return "Your account role does not permit this action \u2014 it needs the account owner or an admin.";
|
|
75
|
+
default:
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
if (err.status === 402) {
|
|
79
|
+
return `This feature requires a higher plan (${err.message}). Upgrade your SentientUI plan, then try again.`;
|
|
80
|
+
}
|
|
81
|
+
if (err.status === 403) {
|
|
82
|
+
return `Access denied (${err.message}). Check that your key or login has access to this project.`;
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
function withApiErrorGuidance(fn) {
|
|
87
|
+
return async (args) => {
|
|
88
|
+
try {
|
|
89
|
+
return await fn(args);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
if (err instanceof ApiError) {
|
|
92
|
+
const guidance = apiErrorGuidance(err);
|
|
93
|
+
if (guidance) {
|
|
94
|
+
return { content: [{ type: "text", text: guidance }], isError: true };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/tools/projects.ts
|
|
64
103
|
function createProjectGuidance(err) {
|
|
65
104
|
switch (err.message) {
|
|
66
105
|
case "insufficient_scope":
|
|
@@ -84,16 +123,16 @@ function registerProjectTools(server, client) {
|
|
|
84
123
|
title: "Create project",
|
|
85
124
|
description: "Create a NEW SentientUI project (onboarding). Returns the project id and its pk_ public key for the SDK. Requires an account login: this works when connected via OAuth (the hosted MCP URL) but NOT with a project-scoped sk_ server key or an anonymous demo token. After it succeeds, call get_integration_guide and help the user install @sentientui/react with the returned key.",
|
|
86
125
|
inputSchema: {
|
|
87
|
-
name:
|
|
88
|
-
contextType:
|
|
89
|
-
framework:
|
|
90
|
-
websiteUrl:
|
|
126
|
+
name: import_zod2.z.string().min(1).describe("Human-readable project name"),
|
|
127
|
+
contextType: import_zod2.z.enum(["saas", "ecommerce", "marketing", "landing", "internal"]).optional().describe("What kind of product this is; defaults to saas"),
|
|
128
|
+
framework: import_zod2.z.enum(["next", "react", "core"]).optional().describe("How the site is built \u2014 next, react, or core (website builder/CMS); defaults to next"),
|
|
129
|
+
websiteUrl: import_zod2.z.string().optional().describe("Production site origin to allow-list so the SDK's events aren't origin-blocked on day one")
|
|
91
130
|
},
|
|
92
131
|
outputSchema: {
|
|
93
|
-
projectId:
|
|
94
|
-
publicKey:
|
|
95
|
-
name:
|
|
96
|
-
contextType:
|
|
132
|
+
projectId: import_zod2.z.string().describe("The new project UUID"),
|
|
133
|
+
publicKey: import_zod2.z.string().describe("The pk_ public key to configure the SDK with"),
|
|
134
|
+
name: import_zod2.z.string().describe("The project name"),
|
|
135
|
+
contextType: import_zod2.z.string().describe("The resolved context type")
|
|
97
136
|
},
|
|
98
137
|
annotations: {
|
|
99
138
|
readOnlyHint: false,
|
|
@@ -110,7 +149,7 @@ function registerProjectTools(server, client) {
|
|
|
110
149
|
framework,
|
|
111
150
|
origin: websiteUrl
|
|
112
151
|
});
|
|
113
|
-
const resolvedContextType = contextType
|
|
152
|
+
const resolvedContextType = contextType != null ? contextType : "saas";
|
|
114
153
|
return {
|
|
115
154
|
content: [{
|
|
116
155
|
type: "text",
|
|
@@ -145,12 +184,12 @@ function registerProjectTools(server, client) {
|
|
|
145
184
|
description: "List all SentientUI projects for the authenticated account.",
|
|
146
185
|
inputSchema: {},
|
|
147
186
|
outputSchema: {
|
|
148
|
-
projects:
|
|
149
|
-
|
|
150
|
-
id:
|
|
151
|
-
name:
|
|
152
|
-
contextType:
|
|
153
|
-
createdAt:
|
|
187
|
+
projects: import_zod2.z.array(
|
|
188
|
+
import_zod2.z.object({
|
|
189
|
+
id: import_zod2.z.string().describe("Project UUID"),
|
|
190
|
+
name: import_zod2.z.string(),
|
|
191
|
+
contextType: import_zod2.z.string(),
|
|
192
|
+
createdAt: import_zod2.z.string().describe("ISO date (YYYY-MM-DD)")
|
|
154
193
|
})
|
|
155
194
|
).describe("All projects for the account (empty if none)")
|
|
156
195
|
},
|
|
@@ -160,7 +199,7 @@ function registerProjectTools(server, client) {
|
|
|
160
199
|
openWorldHint: false
|
|
161
200
|
}
|
|
162
201
|
},
|
|
163
|
-
async () => {
|
|
202
|
+
withApiErrorGuidance(async () => {
|
|
164
203
|
const projects = await client.get("/projects");
|
|
165
204
|
const text = projects.length === 0 ? "No projects found." : projects.map(
|
|
166
205
|
(p) => `- ${p.name} (id: ${p.id}, type: ${p.context_type}, created: ${p.created_at.slice(0, 10)})`
|
|
@@ -176,7 +215,7 @@ function registerProjectTools(server, client) {
|
|
|
176
215
|
}))
|
|
177
216
|
}
|
|
178
217
|
};
|
|
179
|
-
}
|
|
218
|
+
})
|
|
180
219
|
);
|
|
181
220
|
server.registerTool(
|
|
182
221
|
"get_project_stats",
|
|
@@ -185,11 +224,11 @@ function registerProjectTools(server, client) {
|
|
|
185
224
|
description: "Get health stats for a project: event volume, session count, agent calls, and status.",
|
|
186
225
|
inputSchema: { projectId: projectIdSchema },
|
|
187
226
|
outputSchema: {
|
|
188
|
-
status:
|
|
189
|
-
events24h:
|
|
190
|
-
sessions24h:
|
|
191
|
-
agentCalls:
|
|
192
|
-
lastEventAt:
|
|
227
|
+
status: import_zod2.z.string().describe("Overall project health status"),
|
|
228
|
+
events24h: import_zod2.z.number().describe("Events in the last 24 hours"),
|
|
229
|
+
sessions24h: import_zod2.z.number().describe("Sessions in the last 24 hours"),
|
|
230
|
+
agentCalls: import_zod2.z.number().describe("Total agent (MCP/API) calls"),
|
|
231
|
+
lastEventAt: import_zod2.z.string().nullable().describe("ISO timestamp of the last event, or null")
|
|
193
232
|
},
|
|
194
233
|
annotations: {
|
|
195
234
|
readOnlyHint: true,
|
|
@@ -197,7 +236,8 @@ function registerProjectTools(server, client) {
|
|
|
197
236
|
openWorldHint: false
|
|
198
237
|
}
|
|
199
238
|
},
|
|
200
|
-
async ({ projectId }) => {
|
|
239
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
240
|
+
var _a2;
|
|
201
241
|
const id = encodeURIComponent(projectId);
|
|
202
242
|
const stats = await client.get(`/projects/${id}/health`);
|
|
203
243
|
const text = [
|
|
@@ -205,7 +245,7 @@ function registerProjectTools(server, client) {
|
|
|
205
245
|
`Events (24h): ${stats.events24h}`,
|
|
206
246
|
`Sessions (24h): ${stats.sessions24h}`,
|
|
207
247
|
`Agent calls (total): ${stats.agentCalls}`,
|
|
208
|
-
`Last event: ${stats.lastEventAt
|
|
248
|
+
`Last event: ${(_a2 = stats.lastEventAt) != null ? _a2 : "never"}`
|
|
209
249
|
].join("\n");
|
|
210
250
|
return {
|
|
211
251
|
content: [{ type: "text", text }],
|
|
@@ -217,12 +257,12 @@ function registerProjectTools(server, client) {
|
|
|
217
257
|
lastEventAt: stats.lastEventAt
|
|
218
258
|
}
|
|
219
259
|
};
|
|
220
|
-
}
|
|
260
|
+
})
|
|
221
261
|
);
|
|
222
262
|
}
|
|
223
263
|
|
|
224
264
|
// src/tools/components.ts
|
|
225
|
-
var
|
|
265
|
+
var import_zod3 = require("zod");
|
|
226
266
|
|
|
227
267
|
// src/ui/templates.ts
|
|
228
268
|
var VIZ_TITLES = {
|
|
@@ -481,21 +521,20 @@ function registerUiResources(server) {
|
|
|
481
521
|
}
|
|
482
522
|
|
|
483
523
|
// src/tools/components.ts
|
|
484
|
-
var projectIdSchema2 = import_zod2.z.string().uuid().describe("The project UUID");
|
|
485
524
|
function registerComponentTools(server, client) {
|
|
486
525
|
server.registerTool(
|
|
487
526
|
"list_components",
|
|
488
527
|
{
|
|
489
528
|
title: "List components",
|
|
490
529
|
description: "List all adaptive components in a project with variant counts and impression totals.",
|
|
491
|
-
inputSchema: { projectId:
|
|
530
|
+
inputSchema: { projectId: projectIdSchema },
|
|
492
531
|
outputSchema: {
|
|
493
|
-
components:
|
|
494
|
-
|
|
495
|
-
componentId:
|
|
496
|
-
variantCount:
|
|
497
|
-
impressions:
|
|
498
|
-
conversions:
|
|
532
|
+
components: import_zod3.z.array(
|
|
533
|
+
import_zod3.z.object({
|
|
534
|
+
componentId: import_zod3.z.string(),
|
|
535
|
+
variantCount: import_zod3.z.number(),
|
|
536
|
+
impressions: import_zod3.z.number(),
|
|
537
|
+
conversions: import_zod3.z.number()
|
|
499
538
|
})
|
|
500
539
|
).describe("Adaptive components in the project (empty if none)")
|
|
501
540
|
},
|
|
@@ -505,7 +544,7 @@ function registerComponentTools(server, client) {
|
|
|
505
544
|
openWorldHint: false
|
|
506
545
|
}
|
|
507
546
|
},
|
|
508
|
-
async ({ projectId }) => {
|
|
547
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
509
548
|
const id = encodeURIComponent(projectId);
|
|
510
549
|
const { components } = await client.get(`/projects/${id}/components`);
|
|
511
550
|
const structuredContent = {
|
|
@@ -526,23 +565,23 @@ function registerComponentTools(server, client) {
|
|
|
526
565
|
(c) => `- ${c.component_id}: ${c.variants.length} variants, ${c.total_impressions} impressions, ${c.total_conversions} conversions`
|
|
527
566
|
).join("\n");
|
|
528
567
|
return { content: [{ type: "text", text }], structuredContent };
|
|
529
|
-
}
|
|
568
|
+
})
|
|
530
569
|
);
|
|
531
570
|
server.registerTool(
|
|
532
571
|
"get_variant_performance",
|
|
533
572
|
{
|
|
534
573
|
title: "Variant performance",
|
|
535
574
|
description: "Get CVR and momentum for all variants in a project over the last 7 days vs prior 7 days.",
|
|
536
|
-
inputSchema: { projectId:
|
|
575
|
+
inputSchema: { projectId: projectIdSchema },
|
|
537
576
|
_meta: uiMeta("variant-performance"),
|
|
538
577
|
outputSchema: {
|
|
539
|
-
variants:
|
|
540
|
-
|
|
541
|
-
variantId:
|
|
542
|
-
currentCvr:
|
|
543
|
-
priorCvr:
|
|
544
|
-
deltaPp:
|
|
545
|
-
momentum:
|
|
578
|
+
variants: import_zod3.z.array(
|
|
579
|
+
import_zod3.z.object({
|
|
580
|
+
variantId: import_zod3.z.string(),
|
|
581
|
+
currentCvr: import_zod3.z.number().describe("Conversion rate over the last 7 days (0-1)"),
|
|
582
|
+
priorCvr: import_zod3.z.number().describe("Conversion rate over the prior 7 days (0-1)"),
|
|
583
|
+
deltaPp: import_zod3.z.number().describe("Change in percentage points"),
|
|
584
|
+
momentum: import_zod3.z.string().describe("Momentum direction: gaining, losing, or stable")
|
|
546
585
|
})
|
|
547
586
|
).describe("Per-variant performance (empty if no data yet)")
|
|
548
587
|
},
|
|
@@ -552,20 +591,24 @@ function registerComponentTools(server, client) {
|
|
|
552
591
|
openWorldHint: false
|
|
553
592
|
}
|
|
554
593
|
},
|
|
555
|
-
async ({ projectId }) => {
|
|
594
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
595
|
+
var _a2, _b, _c;
|
|
556
596
|
const id = encodeURIComponent(projectId);
|
|
557
597
|
const data = await client.get(`/projects/${id}/trends`);
|
|
558
|
-
const momentumMap = new Map((data.momentum
|
|
598
|
+
const momentumMap = new Map(((_a2 = data.momentum) != null ? _a2 : []).map((m) => [m.variantId, m.direction]));
|
|
559
599
|
const structuredContent = {
|
|
560
|
-
variants: (data.cvr
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
600
|
+
variants: ((_b = data.cvr) != null ? _b : []).map((v) => {
|
|
601
|
+
var _a3;
|
|
602
|
+
return {
|
|
603
|
+
variantId: v.variantId,
|
|
604
|
+
currentCvr: v.currentCvr,
|
|
605
|
+
priorCvr: v.priorCvr,
|
|
606
|
+
deltaPp: v.deltaPp,
|
|
607
|
+
momentum: (_a3 = momentumMap.get(v.variantId)) != null ? _a3 : "stable"
|
|
608
|
+
};
|
|
609
|
+
})
|
|
567
610
|
};
|
|
568
|
-
if (!data.cvr
|
|
611
|
+
if (!((_c = data.cvr) == null ? void 0 : _c.length)) {
|
|
569
612
|
return {
|
|
570
613
|
content: [{ type: "text", text: "No variant data available yet." }],
|
|
571
614
|
structuredContent,
|
|
@@ -573,29 +616,31 @@ function registerComponentTools(server, client) {
|
|
|
573
616
|
};
|
|
574
617
|
}
|
|
575
618
|
const text = data.cvr.map(
|
|
576
|
-
(v) =>
|
|
619
|
+
(v) => {
|
|
620
|
+
var _a3;
|
|
621
|
+
return `- ${v.variantId}: CVR ${(v.currentCvr * 100).toFixed(2)}% (prior ${(v.priorCvr * 100).toFixed(2)}%, ${v.deltaPp > 0 ? "+" : ""}${v.deltaPp.toFixed(1)} pp, ${(_a3 = momentumMap.get(v.variantId)) != null ? _a3 : "stable"})`;
|
|
622
|
+
}
|
|
577
623
|
).join("\n");
|
|
578
624
|
return { content: [{ type: "text", text }], structuredContent, _meta: uiMeta("variant-performance") };
|
|
579
|
-
}
|
|
625
|
+
})
|
|
580
626
|
);
|
|
581
627
|
}
|
|
582
628
|
|
|
583
629
|
// src/tools/insights.ts
|
|
584
|
-
var
|
|
585
|
-
var projectIdSchema3 = import_zod3.z.string().uuid().describe("The project UUID");
|
|
630
|
+
var import_zod4 = require("zod");
|
|
586
631
|
function registerInsightTools(server, client) {
|
|
587
632
|
server.registerTool(
|
|
588
633
|
"get_insights",
|
|
589
634
|
{
|
|
590
635
|
title: "Get insights",
|
|
591
636
|
description: "Get the latest AI-generated insights: narrator observations and (Growth tier) advisor recommendations.",
|
|
592
|
-
inputSchema: { projectId:
|
|
637
|
+
inputSchema: { projectId: projectIdSchema },
|
|
593
638
|
outputSchema: {
|
|
594
|
-
status:
|
|
595
|
-
observations:
|
|
596
|
-
recommendations:
|
|
597
|
-
isStale:
|
|
598
|
-
generatedAt:
|
|
639
|
+
status: import_zod4.z.enum(["ok", "empty"]).describe("Whether insights exist yet"),
|
|
640
|
+
observations: import_zod4.z.array(import_zod4.z.string()).describe("Narrator observations"),
|
|
641
|
+
recommendations: import_zod4.z.array(import_zod4.z.string()).describe("Advisor recommendations (Growth tier)"),
|
|
642
|
+
isStale: import_zod4.z.boolean().describe("True when the insights are older than ~6h"),
|
|
643
|
+
generatedAt: import_zod4.z.string().nullable().describe("ISO timestamp the insights were generated, or null")
|
|
599
644
|
},
|
|
600
645
|
annotations: {
|
|
601
646
|
readOnlyHint: true,
|
|
@@ -603,7 +648,8 @@ function registerInsightTools(server, client) {
|
|
|
603
648
|
openWorldHint: false
|
|
604
649
|
}
|
|
605
650
|
},
|
|
606
|
-
async ({ projectId }) => {
|
|
651
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
652
|
+
var _a2, _b, _c, _d;
|
|
607
653
|
const id = encodeURIComponent(projectId);
|
|
608
654
|
const data = await client.get(`/projects/${id}/insights`);
|
|
609
655
|
if (data.status === "empty") {
|
|
@@ -618,8 +664,8 @@ function registerInsightTools(server, client) {
|
|
|
618
664
|
}
|
|
619
665
|
};
|
|
620
666
|
}
|
|
621
|
-
const observations = data.narratorBullets
|
|
622
|
-
const recommendations = data.advisorBullets
|
|
667
|
+
const observations = (_a2 = data.narratorBullets) != null ? _a2 : [];
|
|
668
|
+
const recommendations = (_b = data.advisorBullets) != null ? _b : [];
|
|
623
669
|
const lines = [];
|
|
624
670
|
if (data.isStale) lines.push("\u26A0 Insights are stale (>6h old). Consider calling refresh_insights.");
|
|
625
671
|
if (data.generatedAt) lines.push(`Generated: ${new Date(data.generatedAt).toUTCString()}`);
|
|
@@ -637,33 +683,32 @@ function registerInsightTools(server, client) {
|
|
|
637
683
|
status: "ok",
|
|
638
684
|
observations,
|
|
639
685
|
recommendations,
|
|
640
|
-
isStale: data.isStale
|
|
641
|
-
generatedAt: data.generatedAt
|
|
686
|
+
isStale: (_c = data.isStale) != null ? _c : false,
|
|
687
|
+
generatedAt: (_d = data.generatedAt) != null ? _d : null
|
|
642
688
|
}
|
|
643
689
|
};
|
|
644
|
-
}
|
|
690
|
+
})
|
|
645
691
|
);
|
|
646
692
|
}
|
|
647
693
|
|
|
648
694
|
// src/tools/personas.ts
|
|
649
|
-
var
|
|
650
|
-
var projectIdSchema4 = import_zod4.z.string().uuid().describe("The project UUID");
|
|
695
|
+
var import_zod5 = require("zod");
|
|
651
696
|
function registerPersonaTools(server, client) {
|
|
652
697
|
server.registerTool(
|
|
653
698
|
"get_persona_breakdown",
|
|
654
699
|
{
|
|
655
700
|
title: "Persona breakdown",
|
|
656
701
|
description: "Get the distribution of visitor persona clusters with session counts and reliability scores.",
|
|
657
|
-
inputSchema: { projectId:
|
|
702
|
+
inputSchema: { projectId: projectIdSchema },
|
|
658
703
|
_meta: uiMeta("persona-breakdown"),
|
|
659
704
|
outputSchema: {
|
|
660
|
-
totalSessions:
|
|
661
|
-
clusters:
|
|
662
|
-
|
|
663
|
-
label:
|
|
664
|
-
sessionCount:
|
|
665
|
-
sharePct:
|
|
666
|
-
reliability:
|
|
705
|
+
totalSessions: import_zod5.z.number().describe("Total sessions across all clusters"),
|
|
706
|
+
clusters: import_zod5.z.array(
|
|
707
|
+
import_zod5.z.object({
|
|
708
|
+
label: import_zod5.z.string(),
|
|
709
|
+
sessionCount: import_zod5.z.number(),
|
|
710
|
+
sharePct: import_zod5.z.number().describe("Share of total traffic (0-100)"),
|
|
711
|
+
reliability: import_zod5.z.number().describe("Average cluster reliability (0-1)")
|
|
667
712
|
})
|
|
668
713
|
).describe("Persona clusters (empty until enough visitor data)")
|
|
669
714
|
},
|
|
@@ -673,7 +718,7 @@ function registerPersonaTools(server, client) {
|
|
|
673
718
|
openWorldHint: false
|
|
674
719
|
}
|
|
675
720
|
},
|
|
676
|
-
async ({ projectId }) => {
|
|
721
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
677
722
|
const id = encodeURIComponent(projectId);
|
|
678
723
|
const data = await client.get(`/projects/${id}/portraits`);
|
|
679
724
|
const structuredContent = {
|
|
@@ -702,33 +747,32 @@ function registerPersonaTools(server, client) {
|
|
|
702
747
|
})
|
|
703
748
|
];
|
|
704
749
|
return { content: [{ type: "text", text: lines.join("\n") }], structuredContent, _meta: uiMeta("persona-breakdown") };
|
|
705
|
-
}
|
|
750
|
+
})
|
|
706
751
|
);
|
|
707
752
|
}
|
|
708
753
|
|
|
709
754
|
// src/tools/goals.ts
|
|
710
|
-
var
|
|
711
|
-
var projectIdSchema5 = import_zod5.z.string().uuid().describe("The project UUID");
|
|
755
|
+
var import_zod6 = require("zod");
|
|
712
756
|
function registerGoalTools(server, client) {
|
|
713
757
|
server.registerTool(
|
|
714
758
|
"get_goal_funnel",
|
|
715
759
|
{
|
|
716
760
|
title: "Goal funnel",
|
|
717
761
|
description: "Get goal hit counts, unique-session conversion rates, and per-variant breakdown.",
|
|
718
|
-
inputSchema: { projectId:
|
|
762
|
+
inputSchema: { projectId: projectIdSchema },
|
|
719
763
|
_meta: uiMeta("goal-funnel"),
|
|
720
764
|
outputSchema: {
|
|
721
|
-
goals:
|
|
722
|
-
|
|
723
|
-
goalName:
|
|
724
|
-
hits:
|
|
725
|
-
uniqueSessions:
|
|
726
|
-
conversionRate:
|
|
727
|
-
variants:
|
|
728
|
-
|
|
729
|
-
componentId:
|
|
730
|
-
variantId:
|
|
731
|
-
completionRate:
|
|
765
|
+
goals: import_zod6.z.array(
|
|
766
|
+
import_zod6.z.object({
|
|
767
|
+
goalName: import_zod6.z.string(),
|
|
768
|
+
hits: import_zod6.z.number(),
|
|
769
|
+
uniqueSessions: import_zod6.z.number(),
|
|
770
|
+
conversionRate: import_zod6.z.number().describe("Unique-session conversion rate (0-1)"),
|
|
771
|
+
variants: import_zod6.z.array(
|
|
772
|
+
import_zod6.z.object({
|
|
773
|
+
componentId: import_zod6.z.string(),
|
|
774
|
+
variantId: import_zod6.z.string(),
|
|
775
|
+
completionRate: import_zod6.z.number().describe("Completion rate per assigned session (0-1)")
|
|
732
776
|
})
|
|
733
777
|
).describe("Per-variant breakdown")
|
|
734
778
|
})
|
|
@@ -740,7 +784,7 @@ function registerGoalTools(server, client) {
|
|
|
740
784
|
openWorldHint: false
|
|
741
785
|
}
|
|
742
786
|
},
|
|
743
|
-
async ({ projectId }) => {
|
|
787
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
744
788
|
const id = encodeURIComponent(projectId);
|
|
745
789
|
const data = await client.get(`/projects/${id}/goals`);
|
|
746
790
|
const structuredContent = {
|
|
@@ -769,26 +813,25 @@ function registerGoalTools(server, client) {
|
|
|
769
813
|
""
|
|
770
814
|
]);
|
|
771
815
|
return { content: [{ type: "text", text: lines.join("\n").trim() }], structuredContent, _meta: uiMeta("goal-funnel") };
|
|
772
|
-
}
|
|
816
|
+
})
|
|
773
817
|
);
|
|
774
818
|
}
|
|
775
819
|
|
|
776
820
|
// src/tools/guardrails.ts
|
|
777
|
-
var
|
|
778
|
-
var projectIdSchema6 = import_zod6.z.string().uuid().describe("The project UUID");
|
|
821
|
+
var import_zod7 = require("zod");
|
|
779
822
|
function registerGuardrailTools(server, client) {
|
|
780
823
|
server.registerTool(
|
|
781
824
|
"list_guardrail_events",
|
|
782
825
|
{
|
|
783
826
|
title: "List guardrail events",
|
|
784
827
|
description: "List variants currently paused by the guardrail in the last 24 hours.",
|
|
785
|
-
inputSchema: { projectId:
|
|
828
|
+
inputSchema: { projectId: projectIdSchema },
|
|
786
829
|
outputSchema: {
|
|
787
|
-
events:
|
|
788
|
-
|
|
789
|
-
componentId:
|
|
790
|
-
variantIds:
|
|
791
|
-
pausedAt:
|
|
830
|
+
events: import_zod7.z.array(
|
|
831
|
+
import_zod7.z.object({
|
|
832
|
+
componentId: import_zod7.z.string(),
|
|
833
|
+
variantIds: import_zod7.z.array(import_zod7.z.string()).describe("Variants paused by the guardrail"),
|
|
834
|
+
pausedAt: import_zod7.z.string().nullable().describe("ISO timestamp the pause fired, or null")
|
|
792
835
|
})
|
|
793
836
|
).describe("Guardrail events in the last 24h (empty if none)")
|
|
794
837
|
},
|
|
@@ -798,7 +841,7 @@ function registerGuardrailTools(server, client) {
|
|
|
798
841
|
openWorldHint: false
|
|
799
842
|
}
|
|
800
843
|
},
|
|
801
|
-
async ({ projectId }) => {
|
|
844
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
802
845
|
const id = encodeURIComponent(projectId);
|
|
803
846
|
const data = await client.get(`/projects/${id}/guardrail-events`);
|
|
804
847
|
const structuredContent = {
|
|
@@ -818,28 +861,27 @@ function registerGuardrailTools(server, client) {
|
|
|
818
861
|
(e) => `- ${e.componentId}: variants [${e.variantIds.join(", ")}] paused${e.pausedAt ? ` at ${e.pausedAt}` : ""}`
|
|
819
862
|
);
|
|
820
863
|
return { content: [{ type: "text", text: lines.join("\n") }], structuredContent };
|
|
821
|
-
}
|
|
864
|
+
})
|
|
822
865
|
);
|
|
823
866
|
}
|
|
824
867
|
|
|
825
868
|
// src/tools/layout.ts
|
|
826
|
-
var
|
|
827
|
-
var projectIdSchema7 = import_zod7.z.string().uuid().describe("The project UUID");
|
|
869
|
+
var import_zod8 = require("zod");
|
|
828
870
|
function registerLayoutTools(server, client) {
|
|
829
871
|
server.registerTool(
|
|
830
872
|
"get_layout_stats",
|
|
831
873
|
{
|
|
832
874
|
title: "Layout stats",
|
|
833
875
|
description: "Get per-persona section layout rankings and bandit reward weights.",
|
|
834
|
-
inputSchema: { projectId:
|
|
876
|
+
inputSchema: { projectId: projectIdSchema },
|
|
835
877
|
_meta: uiMeta("layout-stats"),
|
|
836
878
|
outputSchema: {
|
|
837
|
-
layouts:
|
|
838
|
-
|
|
839
|
-
persona:
|
|
840
|
-
layoutOrder:
|
|
841
|
-
pulls:
|
|
842
|
-
avgReward:
|
|
879
|
+
layouts: import_zod8.z.array(
|
|
880
|
+
import_zod8.z.object({
|
|
881
|
+
persona: import_zod8.z.string(),
|
|
882
|
+
layoutOrder: import_zod8.z.array(import_zod8.z.string()).describe("Ranked section order for this persona"),
|
|
883
|
+
pulls: import_zod8.z.number().describe("Number of times this arm was served"),
|
|
884
|
+
avgReward: import_zod8.z.number().describe("Average bandit reward weight")
|
|
843
885
|
})
|
|
844
886
|
).describe("Per-persona layout rankings (empty until enough sessions)")
|
|
845
887
|
},
|
|
@@ -849,7 +891,7 @@ function registerLayoutTools(server, client) {
|
|
|
849
891
|
openWorldHint: false
|
|
850
892
|
}
|
|
851
893
|
},
|
|
852
|
-
async ({ projectId }) => {
|
|
894
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
853
895
|
const id = encodeURIComponent(projectId);
|
|
854
896
|
const stats = await client.get(`/projects/${id}/layout-stats`);
|
|
855
897
|
const structuredContent = {
|
|
@@ -871,13 +913,12 @@ function registerLayoutTools(server, client) {
|
|
|
871
913
|
(s) => `- ${s.persona}: [${s.layoutOrder.join(" \u2192 ")}] (avg reward: ${s.avgReward.toFixed(2)}, ${s.pulls} pulls)`
|
|
872
914
|
).join("\n");
|
|
873
915
|
return { content: [{ type: "text", text }], structuredContent, _meta: uiMeta("layout-stats") };
|
|
874
|
-
}
|
|
916
|
+
})
|
|
875
917
|
);
|
|
876
918
|
}
|
|
877
919
|
|
|
878
920
|
// src/tools/variants.ts
|
|
879
|
-
var
|
|
880
|
-
var projectIdSchema8 = import_zod8.z.string().uuid().describe("The project UUID");
|
|
921
|
+
var import_zod9 = require("zod");
|
|
881
922
|
function registerVariantWriteTools(server, client) {
|
|
882
923
|
server.registerTool(
|
|
883
924
|
"create_variant",
|
|
@@ -885,17 +926,17 @@ function registerVariantWriteTools(server, client) {
|
|
|
885
926
|
title: "Create managed variant",
|
|
886
927
|
description: "Create a NO-CODE managed text variant for a component (content stored in SentientUI, rendered by <AdaptiveText>). Use this ONLY for text-only variants the user wants without a code change. For variants that will live in the codebase (full components \u2014 copy, markup, styling), use get_variant_brief and write the variant in code instead; those auto-register on deploy and do not need create_variant. Requires a paid plan (server keys are Starter+; anonymous demo tokens are read-only).",
|
|
887
928
|
inputSchema: {
|
|
888
|
-
projectId:
|
|
889
|
-
componentId:
|
|
890
|
-
displayName:
|
|
891
|
-
content:
|
|
929
|
+
projectId: projectIdSchema,
|
|
930
|
+
componentId: import_zod9.z.string().describe("The component ID to add a variant to"),
|
|
931
|
+
displayName: import_zod9.z.string().describe("Human-readable name for the new variant"),
|
|
932
|
+
content: import_zod9.z.string().optional().describe("The text content for this managed variant (rendered by <AdaptiveText>). Generate it from get_variant_brief context; omit only to create an empty placeholder to fill in from the dashboard.")
|
|
892
933
|
},
|
|
893
934
|
outputSchema: {
|
|
894
|
-
variantId:
|
|
895
|
-
displayName:
|
|
896
|
-
componentId:
|
|
897
|
-
state:
|
|
898
|
-
hasContent:
|
|
935
|
+
variantId: import_zod9.z.string().describe("The new variant ID"),
|
|
936
|
+
displayName: import_zod9.z.string(),
|
|
937
|
+
componentId: import_zod9.z.string(),
|
|
938
|
+
state: import_zod9.z.literal("draft").describe("New managed variants start in draft state"),
|
|
939
|
+
hasContent: import_zod9.z.boolean().describe("Whether text content was provided at creation")
|
|
899
940
|
},
|
|
900
941
|
annotations: {
|
|
901
942
|
readOnlyHint: false,
|
|
@@ -904,7 +945,7 @@ function registerVariantWriteTools(server, client) {
|
|
|
904
945
|
openWorldHint: false
|
|
905
946
|
}
|
|
906
947
|
},
|
|
907
|
-
async ({ projectId, componentId, displayName, content }) => {
|
|
948
|
+
withApiErrorGuidance(async ({ projectId, componentId, displayName, content }) => {
|
|
908
949
|
const id = encodeURIComponent(projectId);
|
|
909
950
|
const result = await client.post(
|
|
910
951
|
`/projects/${id}/variants`,
|
|
@@ -924,7 +965,7 @@ function registerVariantWriteTools(server, client) {
|
|
|
924
965
|
hasContent: Boolean(content)
|
|
925
966
|
}
|
|
926
967
|
};
|
|
927
|
-
}
|
|
968
|
+
})
|
|
928
969
|
);
|
|
929
970
|
server.registerTool(
|
|
930
971
|
"pause_variant",
|
|
@@ -932,14 +973,14 @@ function registerVariantWriteTools(server, client) {
|
|
|
932
973
|
title: "Pause variant",
|
|
933
974
|
description: "Pause a variant, stopping traffic from being assigned to it.",
|
|
934
975
|
inputSchema: {
|
|
935
|
-
projectId:
|
|
936
|
-
componentId:
|
|
937
|
-
variantId:
|
|
976
|
+
projectId: projectIdSchema,
|
|
977
|
+
componentId: import_zod9.z.string().describe("The component ID"),
|
|
978
|
+
variantId: import_zod9.z.string().describe("The variant ID to pause")
|
|
938
979
|
},
|
|
939
980
|
outputSchema: {
|
|
940
|
-
variantId:
|
|
941
|
-
componentId:
|
|
942
|
-
paused:
|
|
981
|
+
variantId: import_zod9.z.string(),
|
|
982
|
+
componentId: import_zod9.z.string(),
|
|
983
|
+
paused: import_zod9.z.literal(true).describe("The variant is now paused")
|
|
943
984
|
},
|
|
944
985
|
annotations: {
|
|
945
986
|
readOnlyHint: false,
|
|
@@ -948,7 +989,7 @@ function registerVariantWriteTools(server, client) {
|
|
|
948
989
|
openWorldHint: false
|
|
949
990
|
}
|
|
950
991
|
},
|
|
951
|
-
async ({ projectId, componentId, variantId }) => {
|
|
992
|
+
withApiErrorGuidance(async ({ projectId, componentId, variantId }) => {
|
|
952
993
|
const id = encodeURIComponent(projectId);
|
|
953
994
|
await client.post(`/projects/${id}/variants/pause`, { componentId, variantId });
|
|
954
995
|
return {
|
|
@@ -958,17 +999,17 @@ function registerVariantWriteTools(server, client) {
|
|
|
958
999
|
}],
|
|
959
1000
|
structuredContent: { variantId, componentId, paused: true }
|
|
960
1001
|
};
|
|
961
|
-
}
|
|
1002
|
+
})
|
|
962
1003
|
);
|
|
963
1004
|
server.registerTool(
|
|
964
1005
|
"refresh_insights",
|
|
965
1006
|
{
|
|
966
1007
|
title: "Refresh insights",
|
|
967
1008
|
description: "Trigger fresh AI insight generation for a project. Returns immediately; use get_insights in ~15 seconds to see results.",
|
|
968
|
-
inputSchema: { projectId:
|
|
1009
|
+
inputSchema: { projectId: projectIdSchema },
|
|
969
1010
|
outputSchema: {
|
|
970
|
-
projectId:
|
|
971
|
-
status:
|
|
1011
|
+
projectId: import_zod9.z.string(),
|
|
1012
|
+
status: import_zod9.z.literal("generating").describe("Generation has been triggered")
|
|
972
1013
|
},
|
|
973
1014
|
annotations: {
|
|
974
1015
|
readOnlyHint: false,
|
|
@@ -977,7 +1018,7 @@ function registerVariantWriteTools(server, client) {
|
|
|
977
1018
|
openWorldHint: false
|
|
978
1019
|
}
|
|
979
1020
|
},
|
|
980
|
-
async ({ projectId }) => {
|
|
1021
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
981
1022
|
const id = encodeURIComponent(projectId);
|
|
982
1023
|
await client.post(`/projects/${id}/insights/refresh`);
|
|
983
1024
|
return {
|
|
@@ -987,13 +1028,12 @@ function registerVariantWriteTools(server, client) {
|
|
|
987
1028
|
}],
|
|
988
1029
|
structuredContent: { projectId, status: "generating" }
|
|
989
1030
|
};
|
|
990
|
-
}
|
|
1031
|
+
})
|
|
991
1032
|
);
|
|
992
1033
|
}
|
|
993
1034
|
|
|
994
1035
|
// src/tools/variant-brief.ts
|
|
995
|
-
var
|
|
996
|
-
var projectIdSchema9 = import_zod9.z.string().uuid().describe("The project UUID");
|
|
1036
|
+
var import_zod10 = require("zod");
|
|
997
1037
|
var GOAL_TARGET = 500;
|
|
998
1038
|
var BEST_PRACTICE_PRIORS = {
|
|
999
1039
|
ecommerce: [
|
|
@@ -1031,11 +1071,12 @@ var GENERIC_PRIORS = [
|
|
|
1031
1071
|
"Add one credible proof point near the action."
|
|
1032
1072
|
];
|
|
1033
1073
|
function priorsFor(contextType) {
|
|
1034
|
-
|
|
1074
|
+
var _a2;
|
|
1075
|
+
return (_a2 = BEST_PRACTICE_PRIORS[contextType]) != null ? _a2 : GENERIC_PRIORS;
|
|
1035
1076
|
}
|
|
1036
1077
|
function computeDataState(impressions, insights, avgReliability) {
|
|
1037
1078
|
if (impressions === 0) return "empty";
|
|
1038
|
-
const insightsReady = insights
|
|
1079
|
+
const insightsReady = (insights == null ? void 0 : insights.status) === "ok" && !insights.isStale;
|
|
1039
1080
|
const reliable = avgReliability === null || avgReliability >= 0.3;
|
|
1040
1081
|
if (impressions < GOAL_TARGET || !insightsReady || !reliable) return "collecting";
|
|
1041
1082
|
return "sufficient";
|
|
@@ -1053,7 +1094,7 @@ function guidanceFor(dataState, contextType) {
|
|
|
1053
1094
|
async function settled(p) {
|
|
1054
1095
|
try {
|
|
1055
1096
|
return await p;
|
|
1056
|
-
} catch {
|
|
1097
|
+
} catch (e) {
|
|
1057
1098
|
return null;
|
|
1058
1099
|
}
|
|
1059
1100
|
}
|
|
@@ -1064,16 +1105,16 @@ function registerVariantBriefTools(server, client) {
|
|
|
1064
1105
|
title: "Variant brief",
|
|
1065
1106
|
description: "Get an insight-driven brief for creating a new CODE-NATIVE variant of a component. Returns current variant performance, audience, insights, a data-sufficiency assessment (with a best-practice fallback when there is no data yet), and step-by-step instructions for writing the variant in the customer's code. Use this instead of create_variant when the variant will live in the codebase.",
|
|
1066
1107
|
inputSchema: {
|
|
1067
|
-
projectId:
|
|
1068
|
-
componentId:
|
|
1108
|
+
projectId: projectIdSchema,
|
|
1109
|
+
componentId: import_zod10.z.string().describe('The component ID to write a new variant for (matches <Adaptive id="...">).')
|
|
1069
1110
|
},
|
|
1070
1111
|
outputSchema: {
|
|
1071
|
-
componentId:
|
|
1072
|
-
contextType:
|
|
1073
|
-
dataState:
|
|
1074
|
-
existingVariantIds:
|
|
1075
|
-
priors:
|
|
1076
|
-
markdown:
|
|
1112
|
+
componentId: import_zod10.z.string(),
|
|
1113
|
+
contextType: import_zod10.z.string().describe("The project's context type (or 'unknown')"),
|
|
1114
|
+
dataState: import_zod10.z.enum(["sufficient", "collecting", "empty"]).describe("Data-sufficiency assessment"),
|
|
1115
|
+
existingVariantIds: import_zod10.z.array(import_zod10.z.string()).describe("Variant IDs already in use (do not reuse)"),
|
|
1116
|
+
priors: import_zod10.z.array(import_zod10.z.string()).describe("Best-practice priors applied for this context type"),
|
|
1117
|
+
markdown: import_zod10.z.string().describe("The full variant brief in Markdown")
|
|
1077
1118
|
},
|
|
1078
1119
|
annotations: {
|
|
1079
1120
|
readOnlyHint: true,
|
|
@@ -1081,7 +1122,8 @@ function registerVariantBriefTools(server, client) {
|
|
|
1081
1122
|
openWorldHint: false
|
|
1082
1123
|
}
|
|
1083
1124
|
},
|
|
1084
|
-
async ({ projectId, componentId }) => {
|
|
1125
|
+
withApiErrorGuidance(async ({ projectId, componentId }) => {
|
|
1126
|
+
var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
1085
1127
|
const id = encodeURIComponent(projectId);
|
|
1086
1128
|
const [projects, componentsEnvelope, trends, portraits, insights] = await Promise.all([
|
|
1087
1129
|
settled(client.get("/projects")),
|
|
@@ -1091,19 +1133,19 @@ function registerVariantBriefTools(server, client) {
|
|
|
1091
1133
|
settled(client.get(`/projects/${id}/portraits`)),
|
|
1092
1134
|
settled(client.get(`/projects/${id}/insights`))
|
|
1093
1135
|
]);
|
|
1094
|
-
const project = projects
|
|
1095
|
-
const contextType = project
|
|
1096
|
-
const components = componentsEnvelope
|
|
1097
|
-
const component = components.find((c) => c.component_id === componentId)
|
|
1098
|
-
const impressions = component
|
|
1099
|
-
const conversions = component
|
|
1136
|
+
const project = (_a2 = projects == null ? void 0 : projects.find((p) => p.id === projectId)) != null ? _a2 : null;
|
|
1137
|
+
const contextType = (_b = project == null ? void 0 : project.context_type) != null ? _b : "unknown";
|
|
1138
|
+
const components = (_c = componentsEnvelope == null ? void 0 : componentsEnvelope.components) != null ? _c : [];
|
|
1139
|
+
const component = (_d = components.find((c) => c.component_id === componentId)) != null ? _d : null;
|
|
1140
|
+
const impressions = (_e = component == null ? void 0 : component.total_impressions) != null ? _e : 0;
|
|
1141
|
+
const conversions = (_f = component == null ? void 0 : component.total_conversions) != null ? _f : 0;
|
|
1100
1142
|
const componentCvr = impressions > 0 ? conversions / impressions * 100 : 0;
|
|
1101
|
-
const existingVariantIds = component
|
|
1143
|
+
const existingVariantIds = (_g = component == null ? void 0 : component.variants.map((v) => v.variant_id)) != null ? _g : [];
|
|
1102
1144
|
const variantIdSet = new Set(existingVariantIds);
|
|
1103
|
-
const momentumMap = new Map((trends
|
|
1104
|
-
const variantPerf = (trends
|
|
1105
|
-
const clusters = portraits
|
|
1106
|
-
const totalSessions = portraits
|
|
1145
|
+
const momentumMap = new Map(((_h = trends == null ? void 0 : trends.momentum) != null ? _h : []).map((m) => [m.variantId, m.direction]));
|
|
1146
|
+
const variantPerf = ((_i = trends == null ? void 0 : trends.cvr) != null ? _i : []).filter((v) => variantIdSet.has(v.variantId));
|
|
1147
|
+
const clusters = (_j = portraits == null ? void 0 : portraits.clusters) != null ? _j : [];
|
|
1148
|
+
const totalSessions = (_k = portraits == null ? void 0 : portraits.totalSessions) != null ? _k : 0;
|
|
1107
1149
|
const avgReliability = clusters.length ? clusters.reduce((s, c) => s + c.avgReliability, 0) / clusters.length : null;
|
|
1108
1150
|
const dataState = computeDataState(impressions, insights, avgReliability);
|
|
1109
1151
|
const lines = [];
|
|
@@ -1128,7 +1170,7 @@ function registerVariantBriefTools(server, client) {
|
|
|
1128
1170
|
lines.push("Current variant performance (7d vs prior 7d):");
|
|
1129
1171
|
for (const v of variantPerf) {
|
|
1130
1172
|
lines.push(
|
|
1131
|
-
`- ${v.variantId}: ${(v.currentCvr * 100).toFixed(2)}% CVR (${v.deltaPp > 0 ? "+" : ""}${v.deltaPp.toFixed(1)} pp, ${momentumMap.get(v.variantId)
|
|
1173
|
+
`- ${v.variantId}: ${(v.currentCvr * 100).toFixed(2)}% CVR (${v.deltaPp > 0 ? "+" : ""}${v.deltaPp.toFixed(1)} pp, ${(_l = momentumMap.get(v.variantId)) != null ? _l : "stable"})`
|
|
1132
1174
|
);
|
|
1133
1175
|
}
|
|
1134
1176
|
lines.push("");
|
|
@@ -1143,8 +1185,8 @@ function registerVariantBriefTools(server, client) {
|
|
|
1143
1185
|
}
|
|
1144
1186
|
if (insights && insights.status === "ok") {
|
|
1145
1187
|
if (insights.isStale) lines.push("\u26A0 Insights are stale (>6h). Consider refresh_insights for a fresher read.");
|
|
1146
|
-
const narrator = insights.narratorBullets
|
|
1147
|
-
const advisor = insights.advisorBullets
|
|
1188
|
+
const narrator = (_m = insights.narratorBullets) != null ? _m : [];
|
|
1189
|
+
const advisor = (_n = insights.advisorBullets) != null ? _n : [];
|
|
1148
1190
|
if (narrator.length) {
|
|
1149
1191
|
lines.push("Insights \u2014 observations:");
|
|
1150
1192
|
narrator.forEach((b) => lines.push(`- ${b}`));
|
|
@@ -1184,17 +1226,16 @@ function registerVariantBriefTools(server, client) {
|
|
|
1184
1226
|
markdown
|
|
1185
1227
|
}
|
|
1186
1228
|
};
|
|
1187
|
-
}
|
|
1229
|
+
})
|
|
1188
1230
|
);
|
|
1189
1231
|
}
|
|
1190
1232
|
|
|
1191
1233
|
// src/tools/test-brief.ts
|
|
1192
|
-
var
|
|
1193
|
-
var projectIdSchema10 = import_zod10.z.string().uuid().describe("The project UUID");
|
|
1234
|
+
var import_zod11 = require("zod");
|
|
1194
1235
|
async function settled2(p) {
|
|
1195
1236
|
try {
|
|
1196
1237
|
return await p;
|
|
1197
|
-
} catch {
|
|
1238
|
+
} catch (e) {
|
|
1198
1239
|
return null;
|
|
1199
1240
|
}
|
|
1200
1241
|
}
|
|
@@ -1205,14 +1246,14 @@ function registerTestBriefTools(server, client) {
|
|
|
1205
1246
|
title: "Test brief",
|
|
1206
1247
|
description: "Get a ready-to-paste test for a SentientUI-wrapped component, populated with the component's real variants and goals. This project uses @sentientui/react/testing. Use this so your tests force a specific variant/layout deterministically and never break when the optimizer serves a different version. Returns a React Testing Library example plus the URL-param recipe for E2E (Playwright/Cypress).",
|
|
1207
1248
|
inputSchema: {
|
|
1208
|
-
projectId:
|
|
1209
|
-
componentId:
|
|
1249
|
+
projectId: projectIdSchema,
|
|
1250
|
+
componentId: import_zod11.z.string().describe('The component ID to write a test for (matches <Adaptive id="...">).')
|
|
1210
1251
|
},
|
|
1211
1252
|
outputSchema: {
|
|
1212
|
-
componentId:
|
|
1213
|
-
forcedVariantId:
|
|
1214
|
-
goalName:
|
|
1215
|
-
markdown:
|
|
1253
|
+
componentId: import_zod11.z.string(),
|
|
1254
|
+
forcedVariantId: import_zod11.z.string().describe("The non-control variant the example forces"),
|
|
1255
|
+
goalName: import_zod11.z.string().describe("The goal the example asserts fires"),
|
|
1256
|
+
markdown: import_zod11.z.string().describe("The full test brief in Markdown")
|
|
1216
1257
|
},
|
|
1217
1258
|
annotations: {
|
|
1218
1259
|
readOnlyHint: true,
|
|
@@ -1220,20 +1261,21 @@ function registerTestBriefTools(server, client) {
|
|
|
1220
1261
|
openWorldHint: false
|
|
1221
1262
|
}
|
|
1222
1263
|
},
|
|
1223
|
-
async ({ projectId, componentId }) => {
|
|
1264
|
+
withApiErrorGuidance(async ({ projectId, componentId }) => {
|
|
1265
|
+
var _a2, _b, _c, _d, _e, _f, _g, _h;
|
|
1224
1266
|
const id = encodeURIComponent(projectId);
|
|
1225
1267
|
const [componentsEnvelope, goalsRes] = await Promise.all([
|
|
1226
1268
|
// mgmt API returns a paginated envelope: { components, total, page, limit }.
|
|
1227
1269
|
settled2(client.get(`/projects/${id}/components`)),
|
|
1228
1270
|
settled2(client.get(`/projects/${id}/goals`))
|
|
1229
1271
|
]);
|
|
1230
|
-
const components = componentsEnvelope
|
|
1231
|
-
const component = components.find((c) => c.component_id === componentId)
|
|
1232
|
-
const variantIds = component
|
|
1233
|
-
const goals = Array.isArray(goalsRes) ? goalsRes : goalsRes
|
|
1234
|
-
const goalName = goals[0]
|
|
1235
|
-
const controlId = variantIds[0]
|
|
1236
|
-
const forcedId = variantIds.find((v) => v !== controlId)
|
|
1272
|
+
const components = (_a2 = componentsEnvelope == null ? void 0 : componentsEnvelope.components) != null ? _a2 : [];
|
|
1273
|
+
const component = (_b = components.find((c) => c.component_id === componentId)) != null ? _b : null;
|
|
1274
|
+
const variantIds = (_c = component == null ? void 0 : component.variants.map((v) => v.variant_id)) != null ? _c : [];
|
|
1275
|
+
const goals = Array.isArray(goalsRes) ? goalsRes : (_d = goalsRes == null ? void 0 : goalsRes.goals) != null ? _d : [];
|
|
1276
|
+
const goalName = (_f = (_e = goals[0]) == null ? void 0 : _e.goalName) != null ? _f : "signup";
|
|
1277
|
+
const controlId = (_g = variantIds[0]) != null ? _g : "control";
|
|
1278
|
+
const forcedId = (_h = variantIds.find((v) => v !== controlId)) != null ? _h : "variant_b";
|
|
1237
1279
|
const lines = [];
|
|
1238
1280
|
lines.push(`# Test brief \u2014 ${componentId}`);
|
|
1239
1281
|
lines.push("");
|
|
@@ -1289,12 +1331,12 @@ function registerTestBriefTools(server, client) {
|
|
|
1289
1331
|
content: [{ type: "text", text: markdown }],
|
|
1290
1332
|
structuredContent: { componentId, forcedVariantId: forcedId, goalName, markdown }
|
|
1291
1333
|
};
|
|
1292
|
-
}
|
|
1334
|
+
})
|
|
1293
1335
|
);
|
|
1294
1336
|
}
|
|
1295
1337
|
|
|
1296
1338
|
// src/tools/integration-guide.ts
|
|
1297
|
-
var
|
|
1339
|
+
var import_zod12 = require("zod");
|
|
1298
1340
|
var GUIDE = `# SentientUI integration guide \u2014 the adaptive ladder
|
|
1299
1341
|
|
|
1300
1342
|
SentientUI adapts a site per visitor type (personas: buyer, researcher, deal_seeker, browser,
|
|
@@ -1369,7 +1411,7 @@ function registerIntegrationGuideTools(server) {
|
|
|
1369
1411
|
description: "Get the SentientUI adaptive-ladder integration guide: setup (keyless and keyed) plus copy-pasteable examples for every rung (Style, Swap, Reorder). Use this to integrate SentientUI into a codebase.",
|
|
1370
1412
|
inputSchema: {},
|
|
1371
1413
|
outputSchema: {
|
|
1372
|
-
guide:
|
|
1414
|
+
guide: import_zod12.z.string().describe("The full integration guide in Markdown")
|
|
1373
1415
|
},
|
|
1374
1416
|
annotations: {
|
|
1375
1417
|
readOnlyHint: true,
|
|
@@ -1427,11 +1469,12 @@ var import_node_path = require("path");
|
|
|
1427
1469
|
var import_node_os = require("os");
|
|
1428
1470
|
var CONFIG_DIR = (0, import_node_path.join)((0, import_node_os.homedir)(), ".config", "sentientui");
|
|
1429
1471
|
var CONFIG_FILE = (0, import_node_path.join)(CONFIG_DIR, "mcp-anon.json");
|
|
1430
|
-
var
|
|
1472
|
+
var _a;
|
|
1473
|
+
var API_BASE = (_a = process.env.SENTIENTUI_API_URL) != null ? _a : "https://api.sentient-ui.com";
|
|
1431
1474
|
function readCachedToken() {
|
|
1432
1475
|
try {
|
|
1433
1476
|
return JSON.parse((0, import_node_fs.readFileSync)(CONFIG_FILE, "utf-8"));
|
|
1434
|
-
} catch {
|
|
1477
|
+
} catch (e) {
|
|
1435
1478
|
return null;
|
|
1436
1479
|
}
|
|
1437
1480
|
}
|
|
@@ -1440,8 +1483,9 @@ function writeCachedToken(cfg) {
|
|
|
1440
1483
|
(0, import_node_fs.writeFileSync)(CONFIG_FILE, JSON.stringify(cfg, null, 2), { encoding: "utf-8", mode: 384 });
|
|
1441
1484
|
}
|
|
1442
1485
|
async function resolveDemoToken() {
|
|
1486
|
+
var _a2;
|
|
1443
1487
|
const cached = readCachedToken();
|
|
1444
|
-
if (cached
|
|
1488
|
+
if (cached == null ? void 0 : cached.token) {
|
|
1445
1489
|
process.stderr.write(
|
|
1446
1490
|
`[sentientui-mcp] Running in demo mode (${CONFIG_FILE}). Set SENTIENTUI_API_KEY for full access.
|
|
1447
1491
|
`
|
|
@@ -1452,7 +1496,7 @@ async function resolveDemoToken() {
|
|
|
1452
1496
|
const res = await fetch(`${API_BASE}/v1/mcp/demo`, { method: "POST" });
|
|
1453
1497
|
if (!res.ok) {
|
|
1454
1498
|
const body = await res.json().catch(() => ({}));
|
|
1455
|
-
throw new Error(`Demo provisioning failed: ${String(body.error
|
|
1499
|
+
throw new Error(`Demo provisioning failed: ${String((_a2 = body.error) != null ? _a2 : res.statusText)}`);
|
|
1456
1500
|
}
|
|
1457
1501
|
const data = await res.json();
|
|
1458
1502
|
writeCachedToken({ token: data.token, projectId: data.projectId });
|