@sentientui/mcp 0.7.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-AGEOG4RI.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/lib.cjs
CHANGED
|
@@ -36,7 +36,7 @@ var import_node_module = require("module");
|
|
|
36
36
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
37
37
|
|
|
38
38
|
// src/tools/projects.ts
|
|
39
|
-
var
|
|
39
|
+
var import_zod2 = require("zod");
|
|
40
40
|
|
|
41
41
|
// src/api-client.ts
|
|
42
42
|
var ApiError = class extends Error {
|
|
@@ -45,16 +45,15 @@ var ApiError = class extends Error {
|
|
|
45
45
|
this.status = status;
|
|
46
46
|
this.name = "ApiError";
|
|
47
47
|
}
|
|
48
|
-
status;
|
|
49
48
|
};
|
|
50
49
|
var ApiClient = class {
|
|
51
|
-
baseUrl;
|
|
52
|
-
apiKey;
|
|
53
50
|
constructor(opts) {
|
|
51
|
+
var _a;
|
|
54
52
|
this.apiKey = opts.apiKey;
|
|
55
|
-
this.baseUrl = (opts.baseUrl
|
|
53
|
+
this.baseUrl = ((_a = opts.baseUrl) != null ? _a : "https://api.sentient-ui.com").replace(/\/$/, "");
|
|
56
54
|
}
|
|
57
55
|
async get(path) {
|
|
56
|
+
var _a;
|
|
58
57
|
const res = await fetch(`${this.baseUrl}/v1/mgmt${path}`, {
|
|
59
58
|
headers: {
|
|
60
59
|
authorization: `Bearer ${this.apiKey}`,
|
|
@@ -63,11 +62,12 @@ var ApiClient = class {
|
|
|
63
62
|
});
|
|
64
63
|
if (!res.ok) {
|
|
65
64
|
const body = await res.json().catch(() => ({}));
|
|
66
|
-
throw new ApiError(res.status, String(body.error
|
|
65
|
+
throw new ApiError(res.status, String((_a = body.error) != null ? _a : res.statusText));
|
|
67
66
|
}
|
|
68
67
|
return res.json();
|
|
69
68
|
}
|
|
70
69
|
async post(path, body) {
|
|
70
|
+
var _a;
|
|
71
71
|
const res = await fetch(`${this.baseUrl}/v1/mgmt${path}`, {
|
|
72
72
|
method: "POST",
|
|
73
73
|
headers: {
|
|
@@ -78,14 +78,51 @@ var ApiClient = class {
|
|
|
78
78
|
});
|
|
79
79
|
if (!res.ok) {
|
|
80
80
|
const errBody = await res.json().catch(() => ({}));
|
|
81
|
-
throw new ApiError(res.status, String(errBody.error
|
|
81
|
+
throw new ApiError(res.status, String((_a = errBody.error) != null ? _a : res.statusText));
|
|
82
82
|
}
|
|
83
83
|
return res.json();
|
|
84
84
|
}
|
|
85
85
|
};
|
|
86
86
|
|
|
87
|
-
// src/tools/
|
|
87
|
+
// src/tools/common.ts
|
|
88
|
+
var import_zod = require("zod");
|
|
88
89
|
var projectIdSchema = import_zod.z.string().uuid().describe("The project UUID");
|
|
90
|
+
function apiErrorGuidance(err) {
|
|
91
|
+
switch (err.message) {
|
|
92
|
+
case "insufficient_scope":
|
|
93
|
+
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.";
|
|
94
|
+
case "demo_read_only":
|
|
95
|
+
return "Demo mode is read-only. Create a SentientUI account and sign in (or use a project server key) to make changes.";
|
|
96
|
+
case "insufficient_role":
|
|
97
|
+
return "Your account role does not permit this action \u2014 it needs the account owner or an admin.";
|
|
98
|
+
default:
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
if (err.status === 402) {
|
|
102
|
+
return `This feature requires a higher plan (${err.message}). Upgrade your SentientUI plan, then try again.`;
|
|
103
|
+
}
|
|
104
|
+
if (err.status === 403) {
|
|
105
|
+
return `Access denied (${err.message}). Check that your key or login has access to this project.`;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
function withApiErrorGuidance(fn) {
|
|
110
|
+
return async (args) => {
|
|
111
|
+
try {
|
|
112
|
+
return await fn(args);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
if (err instanceof ApiError) {
|
|
115
|
+
const guidance = apiErrorGuidance(err);
|
|
116
|
+
if (guidance) {
|
|
117
|
+
return { content: [{ type: "text", text: guidance }], isError: true };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/tools/projects.ts
|
|
89
126
|
function createProjectGuidance(err) {
|
|
90
127
|
switch (err.message) {
|
|
91
128
|
case "insufficient_scope":
|
|
@@ -109,16 +146,16 @@ function registerProjectTools(server, client) {
|
|
|
109
146
|
title: "Create project",
|
|
110
147
|
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.",
|
|
111
148
|
inputSchema: {
|
|
112
|
-
name:
|
|
113
|
-
contextType:
|
|
114
|
-
framework:
|
|
115
|
-
websiteUrl:
|
|
149
|
+
name: import_zod2.z.string().min(1).describe("Human-readable project name"),
|
|
150
|
+
contextType: import_zod2.z.enum(["saas", "ecommerce", "marketing", "landing", "internal"]).optional().describe("What kind of product this is; defaults to saas"),
|
|
151
|
+
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"),
|
|
152
|
+
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")
|
|
116
153
|
},
|
|
117
154
|
outputSchema: {
|
|
118
|
-
projectId:
|
|
119
|
-
publicKey:
|
|
120
|
-
name:
|
|
121
|
-
contextType:
|
|
155
|
+
projectId: import_zod2.z.string().describe("The new project UUID"),
|
|
156
|
+
publicKey: import_zod2.z.string().describe("The pk_ public key to configure the SDK with"),
|
|
157
|
+
name: import_zod2.z.string().describe("The project name"),
|
|
158
|
+
contextType: import_zod2.z.string().describe("The resolved context type")
|
|
122
159
|
},
|
|
123
160
|
annotations: {
|
|
124
161
|
readOnlyHint: false,
|
|
@@ -135,7 +172,7 @@ function registerProjectTools(server, client) {
|
|
|
135
172
|
framework,
|
|
136
173
|
origin: websiteUrl
|
|
137
174
|
});
|
|
138
|
-
const resolvedContextType = contextType
|
|
175
|
+
const resolvedContextType = contextType != null ? contextType : "saas";
|
|
139
176
|
return {
|
|
140
177
|
content: [{
|
|
141
178
|
type: "text",
|
|
@@ -170,12 +207,12 @@ function registerProjectTools(server, client) {
|
|
|
170
207
|
description: "List all SentientUI projects for the authenticated account.",
|
|
171
208
|
inputSchema: {},
|
|
172
209
|
outputSchema: {
|
|
173
|
-
projects:
|
|
174
|
-
|
|
175
|
-
id:
|
|
176
|
-
name:
|
|
177
|
-
contextType:
|
|
178
|
-
createdAt:
|
|
210
|
+
projects: import_zod2.z.array(
|
|
211
|
+
import_zod2.z.object({
|
|
212
|
+
id: import_zod2.z.string().describe("Project UUID"),
|
|
213
|
+
name: import_zod2.z.string(),
|
|
214
|
+
contextType: import_zod2.z.string(),
|
|
215
|
+
createdAt: import_zod2.z.string().describe("ISO date (YYYY-MM-DD)")
|
|
179
216
|
})
|
|
180
217
|
).describe("All projects for the account (empty if none)")
|
|
181
218
|
},
|
|
@@ -185,7 +222,7 @@ function registerProjectTools(server, client) {
|
|
|
185
222
|
openWorldHint: false
|
|
186
223
|
}
|
|
187
224
|
},
|
|
188
|
-
async () => {
|
|
225
|
+
withApiErrorGuidance(async () => {
|
|
189
226
|
const projects = await client.get("/projects");
|
|
190
227
|
const text = projects.length === 0 ? "No projects found." : projects.map(
|
|
191
228
|
(p) => `- ${p.name} (id: ${p.id}, type: ${p.context_type}, created: ${p.created_at.slice(0, 10)})`
|
|
@@ -201,7 +238,7 @@ function registerProjectTools(server, client) {
|
|
|
201
238
|
}))
|
|
202
239
|
}
|
|
203
240
|
};
|
|
204
|
-
}
|
|
241
|
+
})
|
|
205
242
|
);
|
|
206
243
|
server.registerTool(
|
|
207
244
|
"get_project_stats",
|
|
@@ -210,11 +247,11 @@ function registerProjectTools(server, client) {
|
|
|
210
247
|
description: "Get health stats for a project: event volume, session count, agent calls, and status.",
|
|
211
248
|
inputSchema: { projectId: projectIdSchema },
|
|
212
249
|
outputSchema: {
|
|
213
|
-
status:
|
|
214
|
-
events24h:
|
|
215
|
-
sessions24h:
|
|
216
|
-
agentCalls:
|
|
217
|
-
lastEventAt:
|
|
250
|
+
status: import_zod2.z.string().describe("Overall project health status"),
|
|
251
|
+
events24h: import_zod2.z.number().describe("Events in the last 24 hours"),
|
|
252
|
+
sessions24h: import_zod2.z.number().describe("Sessions in the last 24 hours"),
|
|
253
|
+
agentCalls: import_zod2.z.number().describe("Total agent (MCP/API) calls"),
|
|
254
|
+
lastEventAt: import_zod2.z.string().nullable().describe("ISO timestamp of the last event, or null")
|
|
218
255
|
},
|
|
219
256
|
annotations: {
|
|
220
257
|
readOnlyHint: true,
|
|
@@ -222,7 +259,8 @@ function registerProjectTools(server, client) {
|
|
|
222
259
|
openWorldHint: false
|
|
223
260
|
}
|
|
224
261
|
},
|
|
225
|
-
async ({ projectId }) => {
|
|
262
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
263
|
+
var _a;
|
|
226
264
|
const id = encodeURIComponent(projectId);
|
|
227
265
|
const stats = await client.get(`/projects/${id}/health`);
|
|
228
266
|
const text = [
|
|
@@ -230,7 +268,7 @@ function registerProjectTools(server, client) {
|
|
|
230
268
|
`Events (24h): ${stats.events24h}`,
|
|
231
269
|
`Sessions (24h): ${stats.sessions24h}`,
|
|
232
270
|
`Agent calls (total): ${stats.agentCalls}`,
|
|
233
|
-
`Last event: ${stats.lastEventAt
|
|
271
|
+
`Last event: ${(_a = stats.lastEventAt) != null ? _a : "never"}`
|
|
234
272
|
].join("\n");
|
|
235
273
|
return {
|
|
236
274
|
content: [{ type: "text", text }],
|
|
@@ -242,12 +280,12 @@ function registerProjectTools(server, client) {
|
|
|
242
280
|
lastEventAt: stats.lastEventAt
|
|
243
281
|
}
|
|
244
282
|
};
|
|
245
|
-
}
|
|
283
|
+
})
|
|
246
284
|
);
|
|
247
285
|
}
|
|
248
286
|
|
|
249
287
|
// src/tools/components.ts
|
|
250
|
-
var
|
|
288
|
+
var import_zod3 = require("zod");
|
|
251
289
|
|
|
252
290
|
// src/ui/templates.ts
|
|
253
291
|
var VIZ_TITLES = {
|
|
@@ -506,21 +544,20 @@ function registerUiResources(server) {
|
|
|
506
544
|
}
|
|
507
545
|
|
|
508
546
|
// src/tools/components.ts
|
|
509
|
-
var projectIdSchema2 = import_zod2.z.string().uuid().describe("The project UUID");
|
|
510
547
|
function registerComponentTools(server, client) {
|
|
511
548
|
server.registerTool(
|
|
512
549
|
"list_components",
|
|
513
550
|
{
|
|
514
551
|
title: "List components",
|
|
515
552
|
description: "List all adaptive components in a project with variant counts and impression totals.",
|
|
516
|
-
inputSchema: { projectId:
|
|
553
|
+
inputSchema: { projectId: projectIdSchema },
|
|
517
554
|
outputSchema: {
|
|
518
|
-
components:
|
|
519
|
-
|
|
520
|
-
componentId:
|
|
521
|
-
variantCount:
|
|
522
|
-
impressions:
|
|
523
|
-
conversions:
|
|
555
|
+
components: import_zod3.z.array(
|
|
556
|
+
import_zod3.z.object({
|
|
557
|
+
componentId: import_zod3.z.string(),
|
|
558
|
+
variantCount: import_zod3.z.number(),
|
|
559
|
+
impressions: import_zod3.z.number(),
|
|
560
|
+
conversions: import_zod3.z.number()
|
|
524
561
|
})
|
|
525
562
|
).describe("Adaptive components in the project (empty if none)")
|
|
526
563
|
},
|
|
@@ -530,7 +567,7 @@ function registerComponentTools(server, client) {
|
|
|
530
567
|
openWorldHint: false
|
|
531
568
|
}
|
|
532
569
|
},
|
|
533
|
-
async ({ projectId }) => {
|
|
570
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
534
571
|
const id = encodeURIComponent(projectId);
|
|
535
572
|
const { components } = await client.get(`/projects/${id}/components`);
|
|
536
573
|
const structuredContent = {
|
|
@@ -551,23 +588,23 @@ function registerComponentTools(server, client) {
|
|
|
551
588
|
(c) => `- ${c.component_id}: ${c.variants.length} variants, ${c.total_impressions} impressions, ${c.total_conversions} conversions`
|
|
552
589
|
).join("\n");
|
|
553
590
|
return { content: [{ type: "text", text }], structuredContent };
|
|
554
|
-
}
|
|
591
|
+
})
|
|
555
592
|
);
|
|
556
593
|
server.registerTool(
|
|
557
594
|
"get_variant_performance",
|
|
558
595
|
{
|
|
559
596
|
title: "Variant performance",
|
|
560
597
|
description: "Get CVR and momentum for all variants in a project over the last 7 days vs prior 7 days.",
|
|
561
|
-
inputSchema: { projectId:
|
|
598
|
+
inputSchema: { projectId: projectIdSchema },
|
|
562
599
|
_meta: uiMeta("variant-performance"),
|
|
563
600
|
outputSchema: {
|
|
564
|
-
variants:
|
|
565
|
-
|
|
566
|
-
variantId:
|
|
567
|
-
currentCvr:
|
|
568
|
-
priorCvr:
|
|
569
|
-
deltaPp:
|
|
570
|
-
momentum:
|
|
601
|
+
variants: import_zod3.z.array(
|
|
602
|
+
import_zod3.z.object({
|
|
603
|
+
variantId: import_zod3.z.string(),
|
|
604
|
+
currentCvr: import_zod3.z.number().describe("Conversion rate over the last 7 days (0-1)"),
|
|
605
|
+
priorCvr: import_zod3.z.number().describe("Conversion rate over the prior 7 days (0-1)"),
|
|
606
|
+
deltaPp: import_zod3.z.number().describe("Change in percentage points"),
|
|
607
|
+
momentum: import_zod3.z.string().describe("Momentum direction: gaining, losing, or stable")
|
|
571
608
|
})
|
|
572
609
|
).describe("Per-variant performance (empty if no data yet)")
|
|
573
610
|
},
|
|
@@ -577,20 +614,24 @@ function registerComponentTools(server, client) {
|
|
|
577
614
|
openWorldHint: false
|
|
578
615
|
}
|
|
579
616
|
},
|
|
580
|
-
async ({ projectId }) => {
|
|
617
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
618
|
+
var _a, _b, _c;
|
|
581
619
|
const id = encodeURIComponent(projectId);
|
|
582
620
|
const data = await client.get(`/projects/${id}/trends`);
|
|
583
|
-
const momentumMap = new Map((data.momentum
|
|
621
|
+
const momentumMap = new Map(((_a = data.momentum) != null ? _a : []).map((m) => [m.variantId, m.direction]));
|
|
584
622
|
const structuredContent = {
|
|
585
|
-
variants: (data.cvr
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
623
|
+
variants: ((_b = data.cvr) != null ? _b : []).map((v) => {
|
|
624
|
+
var _a2;
|
|
625
|
+
return {
|
|
626
|
+
variantId: v.variantId,
|
|
627
|
+
currentCvr: v.currentCvr,
|
|
628
|
+
priorCvr: v.priorCvr,
|
|
629
|
+
deltaPp: v.deltaPp,
|
|
630
|
+
momentum: (_a2 = momentumMap.get(v.variantId)) != null ? _a2 : "stable"
|
|
631
|
+
};
|
|
632
|
+
})
|
|
592
633
|
};
|
|
593
|
-
if (!data.cvr
|
|
634
|
+
if (!((_c = data.cvr) == null ? void 0 : _c.length)) {
|
|
594
635
|
return {
|
|
595
636
|
content: [{ type: "text", text: "No variant data available yet." }],
|
|
596
637
|
structuredContent,
|
|
@@ -598,29 +639,31 @@ function registerComponentTools(server, client) {
|
|
|
598
639
|
};
|
|
599
640
|
}
|
|
600
641
|
const text = data.cvr.map(
|
|
601
|
-
(v) =>
|
|
642
|
+
(v) => {
|
|
643
|
+
var _a2;
|
|
644
|
+
return `- ${v.variantId}: CVR ${(v.currentCvr * 100).toFixed(2)}% (prior ${(v.priorCvr * 100).toFixed(2)}%, ${v.deltaPp > 0 ? "+" : ""}${v.deltaPp.toFixed(1)} pp, ${(_a2 = momentumMap.get(v.variantId)) != null ? _a2 : "stable"})`;
|
|
645
|
+
}
|
|
602
646
|
).join("\n");
|
|
603
647
|
return { content: [{ type: "text", text }], structuredContent, _meta: uiMeta("variant-performance") };
|
|
604
|
-
}
|
|
648
|
+
})
|
|
605
649
|
);
|
|
606
650
|
}
|
|
607
651
|
|
|
608
652
|
// src/tools/insights.ts
|
|
609
|
-
var
|
|
610
|
-
var projectIdSchema3 = import_zod3.z.string().uuid().describe("The project UUID");
|
|
653
|
+
var import_zod4 = require("zod");
|
|
611
654
|
function registerInsightTools(server, client) {
|
|
612
655
|
server.registerTool(
|
|
613
656
|
"get_insights",
|
|
614
657
|
{
|
|
615
658
|
title: "Get insights",
|
|
616
659
|
description: "Get the latest AI-generated insights: narrator observations and (Growth tier) advisor recommendations.",
|
|
617
|
-
inputSchema: { projectId:
|
|
660
|
+
inputSchema: { projectId: projectIdSchema },
|
|
618
661
|
outputSchema: {
|
|
619
|
-
status:
|
|
620
|
-
observations:
|
|
621
|
-
recommendations:
|
|
622
|
-
isStale:
|
|
623
|
-
generatedAt:
|
|
662
|
+
status: import_zod4.z.enum(["ok", "empty"]).describe("Whether insights exist yet"),
|
|
663
|
+
observations: import_zod4.z.array(import_zod4.z.string()).describe("Narrator observations"),
|
|
664
|
+
recommendations: import_zod4.z.array(import_zod4.z.string()).describe("Advisor recommendations (Growth tier)"),
|
|
665
|
+
isStale: import_zod4.z.boolean().describe("True when the insights are older than ~6h"),
|
|
666
|
+
generatedAt: import_zod4.z.string().nullable().describe("ISO timestamp the insights were generated, or null")
|
|
624
667
|
},
|
|
625
668
|
annotations: {
|
|
626
669
|
readOnlyHint: true,
|
|
@@ -628,7 +671,8 @@ function registerInsightTools(server, client) {
|
|
|
628
671
|
openWorldHint: false
|
|
629
672
|
}
|
|
630
673
|
},
|
|
631
|
-
async ({ projectId }) => {
|
|
674
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
675
|
+
var _a, _b, _c, _d;
|
|
632
676
|
const id = encodeURIComponent(projectId);
|
|
633
677
|
const data = await client.get(`/projects/${id}/insights`);
|
|
634
678
|
if (data.status === "empty") {
|
|
@@ -643,8 +687,8 @@ function registerInsightTools(server, client) {
|
|
|
643
687
|
}
|
|
644
688
|
};
|
|
645
689
|
}
|
|
646
|
-
const observations = data.narratorBullets
|
|
647
|
-
const recommendations = data.advisorBullets
|
|
690
|
+
const observations = (_a = data.narratorBullets) != null ? _a : [];
|
|
691
|
+
const recommendations = (_b = data.advisorBullets) != null ? _b : [];
|
|
648
692
|
const lines = [];
|
|
649
693
|
if (data.isStale) lines.push("\u26A0 Insights are stale (>6h old). Consider calling refresh_insights.");
|
|
650
694
|
if (data.generatedAt) lines.push(`Generated: ${new Date(data.generatedAt).toUTCString()}`);
|
|
@@ -662,33 +706,32 @@ function registerInsightTools(server, client) {
|
|
|
662
706
|
status: "ok",
|
|
663
707
|
observations,
|
|
664
708
|
recommendations,
|
|
665
|
-
isStale: data.isStale
|
|
666
|
-
generatedAt: data.generatedAt
|
|
709
|
+
isStale: (_c = data.isStale) != null ? _c : false,
|
|
710
|
+
generatedAt: (_d = data.generatedAt) != null ? _d : null
|
|
667
711
|
}
|
|
668
712
|
};
|
|
669
|
-
}
|
|
713
|
+
})
|
|
670
714
|
);
|
|
671
715
|
}
|
|
672
716
|
|
|
673
717
|
// src/tools/personas.ts
|
|
674
|
-
var
|
|
675
|
-
var projectIdSchema4 = import_zod4.z.string().uuid().describe("The project UUID");
|
|
718
|
+
var import_zod5 = require("zod");
|
|
676
719
|
function registerPersonaTools(server, client) {
|
|
677
720
|
server.registerTool(
|
|
678
721
|
"get_persona_breakdown",
|
|
679
722
|
{
|
|
680
723
|
title: "Persona breakdown",
|
|
681
724
|
description: "Get the distribution of visitor persona clusters with session counts and reliability scores.",
|
|
682
|
-
inputSchema: { projectId:
|
|
725
|
+
inputSchema: { projectId: projectIdSchema },
|
|
683
726
|
_meta: uiMeta("persona-breakdown"),
|
|
684
727
|
outputSchema: {
|
|
685
|
-
totalSessions:
|
|
686
|
-
clusters:
|
|
687
|
-
|
|
688
|
-
label:
|
|
689
|
-
sessionCount:
|
|
690
|
-
sharePct:
|
|
691
|
-
reliability:
|
|
728
|
+
totalSessions: import_zod5.z.number().describe("Total sessions across all clusters"),
|
|
729
|
+
clusters: import_zod5.z.array(
|
|
730
|
+
import_zod5.z.object({
|
|
731
|
+
label: import_zod5.z.string(),
|
|
732
|
+
sessionCount: import_zod5.z.number(),
|
|
733
|
+
sharePct: import_zod5.z.number().describe("Share of total traffic (0-100)"),
|
|
734
|
+
reliability: import_zod5.z.number().describe("Average cluster reliability (0-1)")
|
|
692
735
|
})
|
|
693
736
|
).describe("Persona clusters (empty until enough visitor data)")
|
|
694
737
|
},
|
|
@@ -698,7 +741,7 @@ function registerPersonaTools(server, client) {
|
|
|
698
741
|
openWorldHint: false
|
|
699
742
|
}
|
|
700
743
|
},
|
|
701
|
-
async ({ projectId }) => {
|
|
744
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
702
745
|
const id = encodeURIComponent(projectId);
|
|
703
746
|
const data = await client.get(`/projects/${id}/portraits`);
|
|
704
747
|
const structuredContent = {
|
|
@@ -727,33 +770,32 @@ function registerPersonaTools(server, client) {
|
|
|
727
770
|
})
|
|
728
771
|
];
|
|
729
772
|
return { content: [{ type: "text", text: lines.join("\n") }], structuredContent, _meta: uiMeta("persona-breakdown") };
|
|
730
|
-
}
|
|
773
|
+
})
|
|
731
774
|
);
|
|
732
775
|
}
|
|
733
776
|
|
|
734
777
|
// src/tools/goals.ts
|
|
735
|
-
var
|
|
736
|
-
var projectIdSchema5 = import_zod5.z.string().uuid().describe("The project UUID");
|
|
778
|
+
var import_zod6 = require("zod");
|
|
737
779
|
function registerGoalTools(server, client) {
|
|
738
780
|
server.registerTool(
|
|
739
781
|
"get_goal_funnel",
|
|
740
782
|
{
|
|
741
783
|
title: "Goal funnel",
|
|
742
784
|
description: "Get goal hit counts, unique-session conversion rates, and per-variant breakdown.",
|
|
743
|
-
inputSchema: { projectId:
|
|
785
|
+
inputSchema: { projectId: projectIdSchema },
|
|
744
786
|
_meta: uiMeta("goal-funnel"),
|
|
745
787
|
outputSchema: {
|
|
746
|
-
goals:
|
|
747
|
-
|
|
748
|
-
goalName:
|
|
749
|
-
hits:
|
|
750
|
-
uniqueSessions:
|
|
751
|
-
conversionRate:
|
|
752
|
-
variants:
|
|
753
|
-
|
|
754
|
-
componentId:
|
|
755
|
-
variantId:
|
|
756
|
-
completionRate:
|
|
788
|
+
goals: import_zod6.z.array(
|
|
789
|
+
import_zod6.z.object({
|
|
790
|
+
goalName: import_zod6.z.string(),
|
|
791
|
+
hits: import_zod6.z.number(),
|
|
792
|
+
uniqueSessions: import_zod6.z.number(),
|
|
793
|
+
conversionRate: import_zod6.z.number().describe("Unique-session conversion rate (0-1)"),
|
|
794
|
+
variants: import_zod6.z.array(
|
|
795
|
+
import_zod6.z.object({
|
|
796
|
+
componentId: import_zod6.z.string(),
|
|
797
|
+
variantId: import_zod6.z.string(),
|
|
798
|
+
completionRate: import_zod6.z.number().describe("Completion rate per assigned session (0-1)")
|
|
757
799
|
})
|
|
758
800
|
).describe("Per-variant breakdown")
|
|
759
801
|
})
|
|
@@ -765,7 +807,7 @@ function registerGoalTools(server, client) {
|
|
|
765
807
|
openWorldHint: false
|
|
766
808
|
}
|
|
767
809
|
},
|
|
768
|
-
async ({ projectId }) => {
|
|
810
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
769
811
|
const id = encodeURIComponent(projectId);
|
|
770
812
|
const data = await client.get(`/projects/${id}/goals`);
|
|
771
813
|
const structuredContent = {
|
|
@@ -794,26 +836,25 @@ function registerGoalTools(server, client) {
|
|
|
794
836
|
""
|
|
795
837
|
]);
|
|
796
838
|
return { content: [{ type: "text", text: lines.join("\n").trim() }], structuredContent, _meta: uiMeta("goal-funnel") };
|
|
797
|
-
}
|
|
839
|
+
})
|
|
798
840
|
);
|
|
799
841
|
}
|
|
800
842
|
|
|
801
843
|
// src/tools/guardrails.ts
|
|
802
|
-
var
|
|
803
|
-
var projectIdSchema6 = import_zod6.z.string().uuid().describe("The project UUID");
|
|
844
|
+
var import_zod7 = require("zod");
|
|
804
845
|
function registerGuardrailTools(server, client) {
|
|
805
846
|
server.registerTool(
|
|
806
847
|
"list_guardrail_events",
|
|
807
848
|
{
|
|
808
849
|
title: "List guardrail events",
|
|
809
850
|
description: "List variants currently paused by the guardrail in the last 24 hours.",
|
|
810
|
-
inputSchema: { projectId:
|
|
851
|
+
inputSchema: { projectId: projectIdSchema },
|
|
811
852
|
outputSchema: {
|
|
812
|
-
events:
|
|
813
|
-
|
|
814
|
-
componentId:
|
|
815
|
-
variantIds:
|
|
816
|
-
pausedAt:
|
|
853
|
+
events: import_zod7.z.array(
|
|
854
|
+
import_zod7.z.object({
|
|
855
|
+
componentId: import_zod7.z.string(),
|
|
856
|
+
variantIds: import_zod7.z.array(import_zod7.z.string()).describe("Variants paused by the guardrail"),
|
|
857
|
+
pausedAt: import_zod7.z.string().nullable().describe("ISO timestamp the pause fired, or null")
|
|
817
858
|
})
|
|
818
859
|
).describe("Guardrail events in the last 24h (empty if none)")
|
|
819
860
|
},
|
|
@@ -823,7 +864,7 @@ function registerGuardrailTools(server, client) {
|
|
|
823
864
|
openWorldHint: false
|
|
824
865
|
}
|
|
825
866
|
},
|
|
826
|
-
async ({ projectId }) => {
|
|
867
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
827
868
|
const id = encodeURIComponent(projectId);
|
|
828
869
|
const data = await client.get(`/projects/${id}/guardrail-events`);
|
|
829
870
|
const structuredContent = {
|
|
@@ -843,28 +884,27 @@ function registerGuardrailTools(server, client) {
|
|
|
843
884
|
(e) => `- ${e.componentId}: variants [${e.variantIds.join(", ")}] paused${e.pausedAt ? ` at ${e.pausedAt}` : ""}`
|
|
844
885
|
);
|
|
845
886
|
return { content: [{ type: "text", text: lines.join("\n") }], structuredContent };
|
|
846
|
-
}
|
|
887
|
+
})
|
|
847
888
|
);
|
|
848
889
|
}
|
|
849
890
|
|
|
850
891
|
// src/tools/layout.ts
|
|
851
|
-
var
|
|
852
|
-
var projectIdSchema7 = import_zod7.z.string().uuid().describe("The project UUID");
|
|
892
|
+
var import_zod8 = require("zod");
|
|
853
893
|
function registerLayoutTools(server, client) {
|
|
854
894
|
server.registerTool(
|
|
855
895
|
"get_layout_stats",
|
|
856
896
|
{
|
|
857
897
|
title: "Layout stats",
|
|
858
898
|
description: "Get per-persona section layout rankings and bandit reward weights.",
|
|
859
|
-
inputSchema: { projectId:
|
|
899
|
+
inputSchema: { projectId: projectIdSchema },
|
|
860
900
|
_meta: uiMeta("layout-stats"),
|
|
861
901
|
outputSchema: {
|
|
862
|
-
layouts:
|
|
863
|
-
|
|
864
|
-
persona:
|
|
865
|
-
layoutOrder:
|
|
866
|
-
pulls:
|
|
867
|
-
avgReward:
|
|
902
|
+
layouts: import_zod8.z.array(
|
|
903
|
+
import_zod8.z.object({
|
|
904
|
+
persona: import_zod8.z.string(),
|
|
905
|
+
layoutOrder: import_zod8.z.array(import_zod8.z.string()).describe("Ranked section order for this persona"),
|
|
906
|
+
pulls: import_zod8.z.number().describe("Number of times this arm was served"),
|
|
907
|
+
avgReward: import_zod8.z.number().describe("Average bandit reward weight")
|
|
868
908
|
})
|
|
869
909
|
).describe("Per-persona layout rankings (empty until enough sessions)")
|
|
870
910
|
},
|
|
@@ -874,7 +914,7 @@ function registerLayoutTools(server, client) {
|
|
|
874
914
|
openWorldHint: false
|
|
875
915
|
}
|
|
876
916
|
},
|
|
877
|
-
async ({ projectId }) => {
|
|
917
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
878
918
|
const id = encodeURIComponent(projectId);
|
|
879
919
|
const stats = await client.get(`/projects/${id}/layout-stats`);
|
|
880
920
|
const structuredContent = {
|
|
@@ -896,13 +936,12 @@ function registerLayoutTools(server, client) {
|
|
|
896
936
|
(s) => `- ${s.persona}: [${s.layoutOrder.join(" \u2192 ")}] (avg reward: ${s.avgReward.toFixed(2)}, ${s.pulls} pulls)`
|
|
897
937
|
).join("\n");
|
|
898
938
|
return { content: [{ type: "text", text }], structuredContent, _meta: uiMeta("layout-stats") };
|
|
899
|
-
}
|
|
939
|
+
})
|
|
900
940
|
);
|
|
901
941
|
}
|
|
902
942
|
|
|
903
943
|
// src/tools/variants.ts
|
|
904
|
-
var
|
|
905
|
-
var projectIdSchema8 = import_zod8.z.string().uuid().describe("The project UUID");
|
|
944
|
+
var import_zod9 = require("zod");
|
|
906
945
|
function registerVariantWriteTools(server, client) {
|
|
907
946
|
server.registerTool(
|
|
908
947
|
"create_variant",
|
|
@@ -910,17 +949,17 @@ function registerVariantWriteTools(server, client) {
|
|
|
910
949
|
title: "Create managed variant",
|
|
911
950
|
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).",
|
|
912
951
|
inputSchema: {
|
|
913
|
-
projectId:
|
|
914
|
-
componentId:
|
|
915
|
-
displayName:
|
|
916
|
-
content:
|
|
952
|
+
projectId: projectIdSchema,
|
|
953
|
+
componentId: import_zod9.z.string().describe("The component ID to add a variant to"),
|
|
954
|
+
displayName: import_zod9.z.string().describe("Human-readable name for the new variant"),
|
|
955
|
+
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.")
|
|
917
956
|
},
|
|
918
957
|
outputSchema: {
|
|
919
|
-
variantId:
|
|
920
|
-
displayName:
|
|
921
|
-
componentId:
|
|
922
|
-
state:
|
|
923
|
-
hasContent:
|
|
958
|
+
variantId: import_zod9.z.string().describe("The new variant ID"),
|
|
959
|
+
displayName: import_zod9.z.string(),
|
|
960
|
+
componentId: import_zod9.z.string(),
|
|
961
|
+
state: import_zod9.z.literal("draft").describe("New managed variants start in draft state"),
|
|
962
|
+
hasContent: import_zod9.z.boolean().describe("Whether text content was provided at creation")
|
|
924
963
|
},
|
|
925
964
|
annotations: {
|
|
926
965
|
readOnlyHint: false,
|
|
@@ -929,7 +968,7 @@ function registerVariantWriteTools(server, client) {
|
|
|
929
968
|
openWorldHint: false
|
|
930
969
|
}
|
|
931
970
|
},
|
|
932
|
-
async ({ projectId, componentId, displayName, content }) => {
|
|
971
|
+
withApiErrorGuidance(async ({ projectId, componentId, displayName, content }) => {
|
|
933
972
|
const id = encodeURIComponent(projectId);
|
|
934
973
|
const result = await client.post(
|
|
935
974
|
`/projects/${id}/variants`,
|
|
@@ -949,7 +988,7 @@ function registerVariantWriteTools(server, client) {
|
|
|
949
988
|
hasContent: Boolean(content)
|
|
950
989
|
}
|
|
951
990
|
};
|
|
952
|
-
}
|
|
991
|
+
})
|
|
953
992
|
);
|
|
954
993
|
server.registerTool(
|
|
955
994
|
"pause_variant",
|
|
@@ -957,14 +996,14 @@ function registerVariantWriteTools(server, client) {
|
|
|
957
996
|
title: "Pause variant",
|
|
958
997
|
description: "Pause a variant, stopping traffic from being assigned to it.",
|
|
959
998
|
inputSchema: {
|
|
960
|
-
projectId:
|
|
961
|
-
componentId:
|
|
962
|
-
variantId:
|
|
999
|
+
projectId: projectIdSchema,
|
|
1000
|
+
componentId: import_zod9.z.string().describe("The component ID"),
|
|
1001
|
+
variantId: import_zod9.z.string().describe("The variant ID to pause")
|
|
963
1002
|
},
|
|
964
1003
|
outputSchema: {
|
|
965
|
-
variantId:
|
|
966
|
-
componentId:
|
|
967
|
-
paused:
|
|
1004
|
+
variantId: import_zod9.z.string(),
|
|
1005
|
+
componentId: import_zod9.z.string(),
|
|
1006
|
+
paused: import_zod9.z.literal(true).describe("The variant is now paused")
|
|
968
1007
|
},
|
|
969
1008
|
annotations: {
|
|
970
1009
|
readOnlyHint: false,
|
|
@@ -973,7 +1012,7 @@ function registerVariantWriteTools(server, client) {
|
|
|
973
1012
|
openWorldHint: false
|
|
974
1013
|
}
|
|
975
1014
|
},
|
|
976
|
-
async ({ projectId, componentId, variantId }) => {
|
|
1015
|
+
withApiErrorGuidance(async ({ projectId, componentId, variantId }) => {
|
|
977
1016
|
const id = encodeURIComponent(projectId);
|
|
978
1017
|
await client.post(`/projects/${id}/variants/pause`, { componentId, variantId });
|
|
979
1018
|
return {
|
|
@@ -983,17 +1022,17 @@ function registerVariantWriteTools(server, client) {
|
|
|
983
1022
|
}],
|
|
984
1023
|
structuredContent: { variantId, componentId, paused: true }
|
|
985
1024
|
};
|
|
986
|
-
}
|
|
1025
|
+
})
|
|
987
1026
|
);
|
|
988
1027
|
server.registerTool(
|
|
989
1028
|
"refresh_insights",
|
|
990
1029
|
{
|
|
991
1030
|
title: "Refresh insights",
|
|
992
1031
|
description: "Trigger fresh AI insight generation for a project. Returns immediately; use get_insights in ~15 seconds to see results.",
|
|
993
|
-
inputSchema: { projectId:
|
|
1032
|
+
inputSchema: { projectId: projectIdSchema },
|
|
994
1033
|
outputSchema: {
|
|
995
|
-
projectId:
|
|
996
|
-
status:
|
|
1034
|
+
projectId: import_zod9.z.string(),
|
|
1035
|
+
status: import_zod9.z.literal("generating").describe("Generation has been triggered")
|
|
997
1036
|
},
|
|
998
1037
|
annotations: {
|
|
999
1038
|
readOnlyHint: false,
|
|
@@ -1002,7 +1041,7 @@ function registerVariantWriteTools(server, client) {
|
|
|
1002
1041
|
openWorldHint: false
|
|
1003
1042
|
}
|
|
1004
1043
|
},
|
|
1005
|
-
async ({ projectId }) => {
|
|
1044
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
1006
1045
|
const id = encodeURIComponent(projectId);
|
|
1007
1046
|
await client.post(`/projects/${id}/insights/refresh`);
|
|
1008
1047
|
return {
|
|
@@ -1012,13 +1051,12 @@ function registerVariantWriteTools(server, client) {
|
|
|
1012
1051
|
}],
|
|
1013
1052
|
structuredContent: { projectId, status: "generating" }
|
|
1014
1053
|
};
|
|
1015
|
-
}
|
|
1054
|
+
})
|
|
1016
1055
|
);
|
|
1017
1056
|
}
|
|
1018
1057
|
|
|
1019
1058
|
// src/tools/variant-brief.ts
|
|
1020
|
-
var
|
|
1021
|
-
var projectIdSchema9 = import_zod9.z.string().uuid().describe("The project UUID");
|
|
1059
|
+
var import_zod10 = require("zod");
|
|
1022
1060
|
var GOAL_TARGET = 500;
|
|
1023
1061
|
var BEST_PRACTICE_PRIORS = {
|
|
1024
1062
|
ecommerce: [
|
|
@@ -1056,11 +1094,12 @@ var GENERIC_PRIORS = [
|
|
|
1056
1094
|
"Add one credible proof point near the action."
|
|
1057
1095
|
];
|
|
1058
1096
|
function priorsFor(contextType) {
|
|
1059
|
-
|
|
1097
|
+
var _a;
|
|
1098
|
+
return (_a = BEST_PRACTICE_PRIORS[contextType]) != null ? _a : GENERIC_PRIORS;
|
|
1060
1099
|
}
|
|
1061
1100
|
function computeDataState(impressions, insights, avgReliability) {
|
|
1062
1101
|
if (impressions === 0) return "empty";
|
|
1063
|
-
const insightsReady = insights
|
|
1102
|
+
const insightsReady = (insights == null ? void 0 : insights.status) === "ok" && !insights.isStale;
|
|
1064
1103
|
const reliable = avgReliability === null || avgReliability >= 0.3;
|
|
1065
1104
|
if (impressions < GOAL_TARGET || !insightsReady || !reliable) return "collecting";
|
|
1066
1105
|
return "sufficient";
|
|
@@ -1078,7 +1117,7 @@ function guidanceFor(dataState, contextType) {
|
|
|
1078
1117
|
async function settled(p) {
|
|
1079
1118
|
try {
|
|
1080
1119
|
return await p;
|
|
1081
|
-
} catch {
|
|
1120
|
+
} catch (e) {
|
|
1082
1121
|
return null;
|
|
1083
1122
|
}
|
|
1084
1123
|
}
|
|
@@ -1089,16 +1128,16 @@ function registerVariantBriefTools(server, client) {
|
|
|
1089
1128
|
title: "Variant brief",
|
|
1090
1129
|
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.",
|
|
1091
1130
|
inputSchema: {
|
|
1092
|
-
projectId:
|
|
1093
|
-
componentId:
|
|
1131
|
+
projectId: projectIdSchema,
|
|
1132
|
+
componentId: import_zod10.z.string().describe('The component ID to write a new variant for (matches <Adaptive id="...">).')
|
|
1094
1133
|
},
|
|
1095
1134
|
outputSchema: {
|
|
1096
|
-
componentId:
|
|
1097
|
-
contextType:
|
|
1098
|
-
dataState:
|
|
1099
|
-
existingVariantIds:
|
|
1100
|
-
priors:
|
|
1101
|
-
markdown:
|
|
1135
|
+
componentId: import_zod10.z.string(),
|
|
1136
|
+
contextType: import_zod10.z.string().describe("The project's context type (or 'unknown')"),
|
|
1137
|
+
dataState: import_zod10.z.enum(["sufficient", "collecting", "empty"]).describe("Data-sufficiency assessment"),
|
|
1138
|
+
existingVariantIds: import_zod10.z.array(import_zod10.z.string()).describe("Variant IDs already in use (do not reuse)"),
|
|
1139
|
+
priors: import_zod10.z.array(import_zod10.z.string()).describe("Best-practice priors applied for this context type"),
|
|
1140
|
+
markdown: import_zod10.z.string().describe("The full variant brief in Markdown")
|
|
1102
1141
|
},
|
|
1103
1142
|
annotations: {
|
|
1104
1143
|
readOnlyHint: true,
|
|
@@ -1106,7 +1145,8 @@ function registerVariantBriefTools(server, client) {
|
|
|
1106
1145
|
openWorldHint: false
|
|
1107
1146
|
}
|
|
1108
1147
|
},
|
|
1109
|
-
async ({ projectId, componentId }) => {
|
|
1148
|
+
withApiErrorGuidance(async ({ projectId, componentId }) => {
|
|
1149
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
1110
1150
|
const id = encodeURIComponent(projectId);
|
|
1111
1151
|
const [projects, componentsEnvelope, trends, portraits, insights] = await Promise.all([
|
|
1112
1152
|
settled(client.get("/projects")),
|
|
@@ -1116,19 +1156,19 @@ function registerVariantBriefTools(server, client) {
|
|
|
1116
1156
|
settled(client.get(`/projects/${id}/portraits`)),
|
|
1117
1157
|
settled(client.get(`/projects/${id}/insights`))
|
|
1118
1158
|
]);
|
|
1119
|
-
const project = projects
|
|
1120
|
-
const contextType = project
|
|
1121
|
-
const components = componentsEnvelope
|
|
1122
|
-
const component = components.find((c) => c.component_id === componentId)
|
|
1123
|
-
const impressions = component
|
|
1124
|
-
const conversions = component
|
|
1159
|
+
const project = (_a = projects == null ? void 0 : projects.find((p) => p.id === projectId)) != null ? _a : null;
|
|
1160
|
+
const contextType = (_b = project == null ? void 0 : project.context_type) != null ? _b : "unknown";
|
|
1161
|
+
const components = (_c = componentsEnvelope == null ? void 0 : componentsEnvelope.components) != null ? _c : [];
|
|
1162
|
+
const component = (_d = components.find((c) => c.component_id === componentId)) != null ? _d : null;
|
|
1163
|
+
const impressions = (_e = component == null ? void 0 : component.total_impressions) != null ? _e : 0;
|
|
1164
|
+
const conversions = (_f = component == null ? void 0 : component.total_conversions) != null ? _f : 0;
|
|
1125
1165
|
const componentCvr = impressions > 0 ? conversions / impressions * 100 : 0;
|
|
1126
|
-
const existingVariantIds = component
|
|
1166
|
+
const existingVariantIds = (_g = component == null ? void 0 : component.variants.map((v) => v.variant_id)) != null ? _g : [];
|
|
1127
1167
|
const variantIdSet = new Set(existingVariantIds);
|
|
1128
|
-
const momentumMap = new Map((trends
|
|
1129
|
-
const variantPerf = (trends
|
|
1130
|
-
const clusters = portraits
|
|
1131
|
-
const totalSessions = portraits
|
|
1168
|
+
const momentumMap = new Map(((_h = trends == null ? void 0 : trends.momentum) != null ? _h : []).map((m) => [m.variantId, m.direction]));
|
|
1169
|
+
const variantPerf = ((_i = trends == null ? void 0 : trends.cvr) != null ? _i : []).filter((v) => variantIdSet.has(v.variantId));
|
|
1170
|
+
const clusters = (_j = portraits == null ? void 0 : portraits.clusters) != null ? _j : [];
|
|
1171
|
+
const totalSessions = (_k = portraits == null ? void 0 : portraits.totalSessions) != null ? _k : 0;
|
|
1132
1172
|
const avgReliability = clusters.length ? clusters.reduce((s, c) => s + c.avgReliability, 0) / clusters.length : null;
|
|
1133
1173
|
const dataState = computeDataState(impressions, insights, avgReliability);
|
|
1134
1174
|
const lines = [];
|
|
@@ -1153,7 +1193,7 @@ function registerVariantBriefTools(server, client) {
|
|
|
1153
1193
|
lines.push("Current variant performance (7d vs prior 7d):");
|
|
1154
1194
|
for (const v of variantPerf) {
|
|
1155
1195
|
lines.push(
|
|
1156
|
-
`- ${v.variantId}: ${(v.currentCvr * 100).toFixed(2)}% CVR (${v.deltaPp > 0 ? "+" : ""}${v.deltaPp.toFixed(1)} pp, ${momentumMap.get(v.variantId)
|
|
1196
|
+
`- ${v.variantId}: ${(v.currentCvr * 100).toFixed(2)}% CVR (${v.deltaPp > 0 ? "+" : ""}${v.deltaPp.toFixed(1)} pp, ${(_l = momentumMap.get(v.variantId)) != null ? _l : "stable"})`
|
|
1157
1197
|
);
|
|
1158
1198
|
}
|
|
1159
1199
|
lines.push("");
|
|
@@ -1168,8 +1208,8 @@ function registerVariantBriefTools(server, client) {
|
|
|
1168
1208
|
}
|
|
1169
1209
|
if (insights && insights.status === "ok") {
|
|
1170
1210
|
if (insights.isStale) lines.push("\u26A0 Insights are stale (>6h). Consider refresh_insights for a fresher read.");
|
|
1171
|
-
const narrator = insights.narratorBullets
|
|
1172
|
-
const advisor = insights.advisorBullets
|
|
1211
|
+
const narrator = (_m = insights.narratorBullets) != null ? _m : [];
|
|
1212
|
+
const advisor = (_n = insights.advisorBullets) != null ? _n : [];
|
|
1173
1213
|
if (narrator.length) {
|
|
1174
1214
|
lines.push("Insights \u2014 observations:");
|
|
1175
1215
|
narrator.forEach((b) => lines.push(`- ${b}`));
|
|
@@ -1209,17 +1249,16 @@ function registerVariantBriefTools(server, client) {
|
|
|
1209
1249
|
markdown
|
|
1210
1250
|
}
|
|
1211
1251
|
};
|
|
1212
|
-
}
|
|
1252
|
+
})
|
|
1213
1253
|
);
|
|
1214
1254
|
}
|
|
1215
1255
|
|
|
1216
1256
|
// src/tools/test-brief.ts
|
|
1217
|
-
var
|
|
1218
|
-
var projectIdSchema10 = import_zod10.z.string().uuid().describe("The project UUID");
|
|
1257
|
+
var import_zod11 = require("zod");
|
|
1219
1258
|
async function settled2(p) {
|
|
1220
1259
|
try {
|
|
1221
1260
|
return await p;
|
|
1222
|
-
} catch {
|
|
1261
|
+
} catch (e) {
|
|
1223
1262
|
return null;
|
|
1224
1263
|
}
|
|
1225
1264
|
}
|
|
@@ -1230,14 +1269,14 @@ function registerTestBriefTools(server, client) {
|
|
|
1230
1269
|
title: "Test brief",
|
|
1231
1270
|
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).",
|
|
1232
1271
|
inputSchema: {
|
|
1233
|
-
projectId:
|
|
1234
|
-
componentId:
|
|
1272
|
+
projectId: projectIdSchema,
|
|
1273
|
+
componentId: import_zod11.z.string().describe('The component ID to write a test for (matches <Adaptive id="...">).')
|
|
1235
1274
|
},
|
|
1236
1275
|
outputSchema: {
|
|
1237
|
-
componentId:
|
|
1238
|
-
forcedVariantId:
|
|
1239
|
-
goalName:
|
|
1240
|
-
markdown:
|
|
1276
|
+
componentId: import_zod11.z.string(),
|
|
1277
|
+
forcedVariantId: import_zod11.z.string().describe("The non-control variant the example forces"),
|
|
1278
|
+
goalName: import_zod11.z.string().describe("The goal the example asserts fires"),
|
|
1279
|
+
markdown: import_zod11.z.string().describe("The full test brief in Markdown")
|
|
1241
1280
|
},
|
|
1242
1281
|
annotations: {
|
|
1243
1282
|
readOnlyHint: true,
|
|
@@ -1245,20 +1284,21 @@ function registerTestBriefTools(server, client) {
|
|
|
1245
1284
|
openWorldHint: false
|
|
1246
1285
|
}
|
|
1247
1286
|
},
|
|
1248
|
-
async ({ projectId, componentId }) => {
|
|
1287
|
+
withApiErrorGuidance(async ({ projectId, componentId }) => {
|
|
1288
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1249
1289
|
const id = encodeURIComponent(projectId);
|
|
1250
1290
|
const [componentsEnvelope, goalsRes] = await Promise.all([
|
|
1251
1291
|
// mgmt API returns a paginated envelope: { components, total, page, limit }.
|
|
1252
1292
|
settled2(client.get(`/projects/${id}/components`)),
|
|
1253
1293
|
settled2(client.get(`/projects/${id}/goals`))
|
|
1254
1294
|
]);
|
|
1255
|
-
const components = componentsEnvelope
|
|
1256
|
-
const component = components.find((c) => c.component_id === componentId)
|
|
1257
|
-
const variantIds = component
|
|
1258
|
-
const goals = Array.isArray(goalsRes) ? goalsRes : goalsRes
|
|
1259
|
-
const goalName = goals[0]
|
|
1260
|
-
const controlId = variantIds[0]
|
|
1261
|
-
const forcedId = variantIds.find((v) => v !== controlId)
|
|
1295
|
+
const components = (_a = componentsEnvelope == null ? void 0 : componentsEnvelope.components) != null ? _a : [];
|
|
1296
|
+
const component = (_b = components.find((c) => c.component_id === componentId)) != null ? _b : null;
|
|
1297
|
+
const variantIds = (_c = component == null ? void 0 : component.variants.map((v) => v.variant_id)) != null ? _c : [];
|
|
1298
|
+
const goals = Array.isArray(goalsRes) ? goalsRes : (_d = goalsRes == null ? void 0 : goalsRes.goals) != null ? _d : [];
|
|
1299
|
+
const goalName = (_f = (_e = goals[0]) == null ? void 0 : _e.goalName) != null ? _f : "signup";
|
|
1300
|
+
const controlId = (_g = variantIds[0]) != null ? _g : "control";
|
|
1301
|
+
const forcedId = (_h = variantIds.find((v) => v !== controlId)) != null ? _h : "variant_b";
|
|
1262
1302
|
const lines = [];
|
|
1263
1303
|
lines.push(`# Test brief \u2014 ${componentId}`);
|
|
1264
1304
|
lines.push("");
|
|
@@ -1314,12 +1354,12 @@ function registerTestBriefTools(server, client) {
|
|
|
1314
1354
|
content: [{ type: "text", text: markdown }],
|
|
1315
1355
|
structuredContent: { componentId, forcedVariantId: forcedId, goalName, markdown }
|
|
1316
1356
|
};
|
|
1317
|
-
}
|
|
1357
|
+
})
|
|
1318
1358
|
);
|
|
1319
1359
|
}
|
|
1320
1360
|
|
|
1321
1361
|
// src/tools/integration-guide.ts
|
|
1322
|
-
var
|
|
1362
|
+
var import_zod12 = require("zod");
|
|
1323
1363
|
var GUIDE = `# SentientUI integration guide \u2014 the adaptive ladder
|
|
1324
1364
|
|
|
1325
1365
|
SentientUI adapts a site per visitor type (personas: buyer, researcher, deal_seeker, browser,
|
|
@@ -1394,7 +1434,7 @@ function registerIntegrationGuideTools(server) {
|
|
|
1394
1434
|
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.",
|
|
1395
1435
|
inputSchema: {},
|
|
1396
1436
|
outputSchema: {
|
|
1397
|
-
guide:
|
|
1437
|
+
guide: import_zod12.z.string().describe("The full integration guide in Markdown")
|
|
1398
1438
|
},
|
|
1399
1439
|
annotations: {
|
|
1400
1440
|
readOnlyHint: true,
|