@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
|
@@ -7,16 +7,15 @@ var ApiError = class extends Error {
|
|
|
7
7
|
this.status = status;
|
|
8
8
|
this.name = "ApiError";
|
|
9
9
|
}
|
|
10
|
-
status;
|
|
11
10
|
};
|
|
12
11
|
var ApiClient = class {
|
|
13
|
-
baseUrl;
|
|
14
|
-
apiKey;
|
|
15
12
|
constructor(opts) {
|
|
13
|
+
var _a;
|
|
16
14
|
this.apiKey = opts.apiKey;
|
|
17
|
-
this.baseUrl = (opts.baseUrl
|
|
15
|
+
this.baseUrl = ((_a = opts.baseUrl) != null ? _a : "https://api.sentient-ui.com").replace(/\/$/, "");
|
|
18
16
|
}
|
|
19
17
|
async get(path) {
|
|
18
|
+
var _a;
|
|
20
19
|
const res = await fetch(`${this.baseUrl}/v1/mgmt${path}`, {
|
|
21
20
|
headers: {
|
|
22
21
|
authorization: `Bearer ${this.apiKey}`,
|
|
@@ -25,11 +24,12 @@ var ApiClient = class {
|
|
|
25
24
|
});
|
|
26
25
|
if (!res.ok) {
|
|
27
26
|
const body = await res.json().catch(() => ({}));
|
|
28
|
-
throw new ApiError(res.status, String(body.error
|
|
27
|
+
throw new ApiError(res.status, String((_a = body.error) != null ? _a : res.statusText));
|
|
29
28
|
}
|
|
30
29
|
return res.json();
|
|
31
30
|
}
|
|
32
31
|
async post(path, body) {
|
|
32
|
+
var _a;
|
|
33
33
|
const res = await fetch(`${this.baseUrl}/v1/mgmt${path}`, {
|
|
34
34
|
method: "POST",
|
|
35
35
|
headers: {
|
|
@@ -40,7 +40,7 @@ var ApiClient = class {
|
|
|
40
40
|
});
|
|
41
41
|
if (!res.ok) {
|
|
42
42
|
const errBody = await res.json().catch(() => ({}));
|
|
43
|
-
throw new ApiError(res.status, String(errBody.error
|
|
43
|
+
throw new ApiError(res.status, String((_a = errBody.error) != null ? _a : res.statusText));
|
|
44
44
|
}
|
|
45
45
|
return res.json();
|
|
46
46
|
}
|
|
@@ -51,8 +51,47 @@ import { createRequire } from "module";
|
|
|
51
51
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
52
52
|
|
|
53
53
|
// src/tools/projects.ts
|
|
54
|
+
import { z as z2 } from "zod";
|
|
55
|
+
|
|
56
|
+
// src/tools/common.ts
|
|
54
57
|
import { z } from "zod";
|
|
55
58
|
var projectIdSchema = z.string().uuid().describe("The project UUID");
|
|
59
|
+
function apiErrorGuidance(err) {
|
|
60
|
+
switch (err.message) {
|
|
61
|
+
case "insufficient_scope":
|
|
62
|
+
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.";
|
|
63
|
+
case "demo_read_only":
|
|
64
|
+
return "Demo mode is read-only. Create a SentientUI account and sign in (or use a project server key) to make changes.";
|
|
65
|
+
case "insufficient_role":
|
|
66
|
+
return "Your account role does not permit this action \u2014 it needs the account owner or an admin.";
|
|
67
|
+
default:
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
if (err.status === 402) {
|
|
71
|
+
return `This feature requires a higher plan (${err.message}). Upgrade your SentientUI plan, then try again.`;
|
|
72
|
+
}
|
|
73
|
+
if (err.status === 403) {
|
|
74
|
+
return `Access denied (${err.message}). Check that your key or login has access to this project.`;
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function withApiErrorGuidance(fn) {
|
|
79
|
+
return async (args) => {
|
|
80
|
+
try {
|
|
81
|
+
return await fn(args);
|
|
82
|
+
} catch (err) {
|
|
83
|
+
if (err instanceof ApiError) {
|
|
84
|
+
const guidance = apiErrorGuidance(err);
|
|
85
|
+
if (guidance) {
|
|
86
|
+
return { content: [{ type: "text", text: guidance }], isError: true };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
throw err;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/tools/projects.ts
|
|
56
95
|
function createProjectGuidance(err) {
|
|
57
96
|
switch (err.message) {
|
|
58
97
|
case "insufficient_scope":
|
|
@@ -76,16 +115,16 @@ function registerProjectTools(server, client) {
|
|
|
76
115
|
title: "Create project",
|
|
77
116
|
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.",
|
|
78
117
|
inputSchema: {
|
|
79
|
-
name:
|
|
80
|
-
contextType:
|
|
81
|
-
framework:
|
|
82
|
-
websiteUrl:
|
|
118
|
+
name: z2.string().min(1).describe("Human-readable project name"),
|
|
119
|
+
contextType: z2.enum(["saas", "ecommerce", "marketing", "landing", "internal"]).optional().describe("What kind of product this is; defaults to saas"),
|
|
120
|
+
framework: z2.enum(["next", "react", "core"]).optional().describe("How the site is built \u2014 next, react, or core (website builder/CMS); defaults to next"),
|
|
121
|
+
websiteUrl: z2.string().optional().describe("Production site origin to allow-list so the SDK's events aren't origin-blocked on day one")
|
|
83
122
|
},
|
|
84
123
|
outputSchema: {
|
|
85
|
-
projectId:
|
|
86
|
-
publicKey:
|
|
87
|
-
name:
|
|
88
|
-
contextType:
|
|
124
|
+
projectId: z2.string().describe("The new project UUID"),
|
|
125
|
+
publicKey: z2.string().describe("The pk_ public key to configure the SDK with"),
|
|
126
|
+
name: z2.string().describe("The project name"),
|
|
127
|
+
contextType: z2.string().describe("The resolved context type")
|
|
89
128
|
},
|
|
90
129
|
annotations: {
|
|
91
130
|
readOnlyHint: false,
|
|
@@ -102,7 +141,7 @@ function registerProjectTools(server, client) {
|
|
|
102
141
|
framework,
|
|
103
142
|
origin: websiteUrl
|
|
104
143
|
});
|
|
105
|
-
const resolvedContextType = contextType
|
|
144
|
+
const resolvedContextType = contextType != null ? contextType : "saas";
|
|
106
145
|
return {
|
|
107
146
|
content: [{
|
|
108
147
|
type: "text",
|
|
@@ -137,12 +176,12 @@ function registerProjectTools(server, client) {
|
|
|
137
176
|
description: "List all SentientUI projects for the authenticated account.",
|
|
138
177
|
inputSchema: {},
|
|
139
178
|
outputSchema: {
|
|
140
|
-
projects:
|
|
141
|
-
|
|
142
|
-
id:
|
|
143
|
-
name:
|
|
144
|
-
contextType:
|
|
145
|
-
createdAt:
|
|
179
|
+
projects: z2.array(
|
|
180
|
+
z2.object({
|
|
181
|
+
id: z2.string().describe("Project UUID"),
|
|
182
|
+
name: z2.string(),
|
|
183
|
+
contextType: z2.string(),
|
|
184
|
+
createdAt: z2.string().describe("ISO date (YYYY-MM-DD)")
|
|
146
185
|
})
|
|
147
186
|
).describe("All projects for the account (empty if none)")
|
|
148
187
|
},
|
|
@@ -152,7 +191,7 @@ function registerProjectTools(server, client) {
|
|
|
152
191
|
openWorldHint: false
|
|
153
192
|
}
|
|
154
193
|
},
|
|
155
|
-
async () => {
|
|
194
|
+
withApiErrorGuidance(async () => {
|
|
156
195
|
const projects = await client.get("/projects");
|
|
157
196
|
const text = projects.length === 0 ? "No projects found." : projects.map(
|
|
158
197
|
(p) => `- ${p.name} (id: ${p.id}, type: ${p.context_type}, created: ${p.created_at.slice(0, 10)})`
|
|
@@ -168,7 +207,7 @@ function registerProjectTools(server, client) {
|
|
|
168
207
|
}))
|
|
169
208
|
}
|
|
170
209
|
};
|
|
171
|
-
}
|
|
210
|
+
})
|
|
172
211
|
);
|
|
173
212
|
server.registerTool(
|
|
174
213
|
"get_project_stats",
|
|
@@ -177,11 +216,11 @@ function registerProjectTools(server, client) {
|
|
|
177
216
|
description: "Get health stats for a project: event volume, session count, agent calls, and status.",
|
|
178
217
|
inputSchema: { projectId: projectIdSchema },
|
|
179
218
|
outputSchema: {
|
|
180
|
-
status:
|
|
181
|
-
events24h:
|
|
182
|
-
sessions24h:
|
|
183
|
-
agentCalls:
|
|
184
|
-
lastEventAt:
|
|
219
|
+
status: z2.string().describe("Overall project health status"),
|
|
220
|
+
events24h: z2.number().describe("Events in the last 24 hours"),
|
|
221
|
+
sessions24h: z2.number().describe("Sessions in the last 24 hours"),
|
|
222
|
+
agentCalls: z2.number().describe("Total agent (MCP/API) calls"),
|
|
223
|
+
lastEventAt: z2.string().nullable().describe("ISO timestamp of the last event, or null")
|
|
185
224
|
},
|
|
186
225
|
annotations: {
|
|
187
226
|
readOnlyHint: true,
|
|
@@ -189,7 +228,8 @@ function registerProjectTools(server, client) {
|
|
|
189
228
|
openWorldHint: false
|
|
190
229
|
}
|
|
191
230
|
},
|
|
192
|
-
async ({ projectId }) => {
|
|
231
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
232
|
+
var _a;
|
|
193
233
|
const id = encodeURIComponent(projectId);
|
|
194
234
|
const stats = await client.get(`/projects/${id}/health`);
|
|
195
235
|
const text = [
|
|
@@ -197,7 +237,7 @@ function registerProjectTools(server, client) {
|
|
|
197
237
|
`Events (24h): ${stats.events24h}`,
|
|
198
238
|
`Sessions (24h): ${stats.sessions24h}`,
|
|
199
239
|
`Agent calls (total): ${stats.agentCalls}`,
|
|
200
|
-
`Last event: ${stats.lastEventAt
|
|
240
|
+
`Last event: ${(_a = stats.lastEventAt) != null ? _a : "never"}`
|
|
201
241
|
].join("\n");
|
|
202
242
|
return {
|
|
203
243
|
content: [{ type: "text", text }],
|
|
@@ -209,12 +249,12 @@ function registerProjectTools(server, client) {
|
|
|
209
249
|
lastEventAt: stats.lastEventAt
|
|
210
250
|
}
|
|
211
251
|
};
|
|
212
|
-
}
|
|
252
|
+
})
|
|
213
253
|
);
|
|
214
254
|
}
|
|
215
255
|
|
|
216
256
|
// src/tools/components.ts
|
|
217
|
-
import { z as
|
|
257
|
+
import { z as z3 } from "zod";
|
|
218
258
|
|
|
219
259
|
// src/ui/templates.ts
|
|
220
260
|
var VIZ_TITLES = {
|
|
@@ -473,21 +513,20 @@ function registerUiResources(server) {
|
|
|
473
513
|
}
|
|
474
514
|
|
|
475
515
|
// src/tools/components.ts
|
|
476
|
-
var projectIdSchema2 = z2.string().uuid().describe("The project UUID");
|
|
477
516
|
function registerComponentTools(server, client) {
|
|
478
517
|
server.registerTool(
|
|
479
518
|
"list_components",
|
|
480
519
|
{
|
|
481
520
|
title: "List components",
|
|
482
521
|
description: "List all adaptive components in a project with variant counts and impression totals.",
|
|
483
|
-
inputSchema: { projectId:
|
|
522
|
+
inputSchema: { projectId: projectIdSchema },
|
|
484
523
|
outputSchema: {
|
|
485
|
-
components:
|
|
486
|
-
|
|
487
|
-
componentId:
|
|
488
|
-
variantCount:
|
|
489
|
-
impressions:
|
|
490
|
-
conversions:
|
|
524
|
+
components: z3.array(
|
|
525
|
+
z3.object({
|
|
526
|
+
componentId: z3.string(),
|
|
527
|
+
variantCount: z3.number(),
|
|
528
|
+
impressions: z3.number(),
|
|
529
|
+
conversions: z3.number()
|
|
491
530
|
})
|
|
492
531
|
).describe("Adaptive components in the project (empty if none)")
|
|
493
532
|
},
|
|
@@ -497,7 +536,7 @@ function registerComponentTools(server, client) {
|
|
|
497
536
|
openWorldHint: false
|
|
498
537
|
}
|
|
499
538
|
},
|
|
500
|
-
async ({ projectId }) => {
|
|
539
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
501
540
|
const id = encodeURIComponent(projectId);
|
|
502
541
|
const { components } = await client.get(`/projects/${id}/components`);
|
|
503
542
|
const structuredContent = {
|
|
@@ -518,23 +557,23 @@ function registerComponentTools(server, client) {
|
|
|
518
557
|
(c) => `- ${c.component_id}: ${c.variants.length} variants, ${c.total_impressions} impressions, ${c.total_conversions} conversions`
|
|
519
558
|
).join("\n");
|
|
520
559
|
return { content: [{ type: "text", text }], structuredContent };
|
|
521
|
-
}
|
|
560
|
+
})
|
|
522
561
|
);
|
|
523
562
|
server.registerTool(
|
|
524
563
|
"get_variant_performance",
|
|
525
564
|
{
|
|
526
565
|
title: "Variant performance",
|
|
527
566
|
description: "Get CVR and momentum for all variants in a project over the last 7 days vs prior 7 days.",
|
|
528
|
-
inputSchema: { projectId:
|
|
567
|
+
inputSchema: { projectId: projectIdSchema },
|
|
529
568
|
_meta: uiMeta("variant-performance"),
|
|
530
569
|
outputSchema: {
|
|
531
|
-
variants:
|
|
532
|
-
|
|
533
|
-
variantId:
|
|
534
|
-
currentCvr:
|
|
535
|
-
priorCvr:
|
|
536
|
-
deltaPp:
|
|
537
|
-
momentum:
|
|
570
|
+
variants: z3.array(
|
|
571
|
+
z3.object({
|
|
572
|
+
variantId: z3.string(),
|
|
573
|
+
currentCvr: z3.number().describe("Conversion rate over the last 7 days (0-1)"),
|
|
574
|
+
priorCvr: z3.number().describe("Conversion rate over the prior 7 days (0-1)"),
|
|
575
|
+
deltaPp: z3.number().describe("Change in percentage points"),
|
|
576
|
+
momentum: z3.string().describe("Momentum direction: gaining, losing, or stable")
|
|
538
577
|
})
|
|
539
578
|
).describe("Per-variant performance (empty if no data yet)")
|
|
540
579
|
},
|
|
@@ -544,20 +583,24 @@ function registerComponentTools(server, client) {
|
|
|
544
583
|
openWorldHint: false
|
|
545
584
|
}
|
|
546
585
|
},
|
|
547
|
-
async ({ projectId }) => {
|
|
586
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
587
|
+
var _a, _b, _c;
|
|
548
588
|
const id = encodeURIComponent(projectId);
|
|
549
589
|
const data = await client.get(`/projects/${id}/trends`);
|
|
550
|
-
const momentumMap = new Map((data.momentum
|
|
590
|
+
const momentumMap = new Map(((_a = data.momentum) != null ? _a : []).map((m) => [m.variantId, m.direction]));
|
|
551
591
|
const structuredContent = {
|
|
552
|
-
variants: (data.cvr
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
592
|
+
variants: ((_b = data.cvr) != null ? _b : []).map((v) => {
|
|
593
|
+
var _a2;
|
|
594
|
+
return {
|
|
595
|
+
variantId: v.variantId,
|
|
596
|
+
currentCvr: v.currentCvr,
|
|
597
|
+
priorCvr: v.priorCvr,
|
|
598
|
+
deltaPp: v.deltaPp,
|
|
599
|
+
momentum: (_a2 = momentumMap.get(v.variantId)) != null ? _a2 : "stable"
|
|
600
|
+
};
|
|
601
|
+
})
|
|
559
602
|
};
|
|
560
|
-
if (!data.cvr
|
|
603
|
+
if (!((_c = data.cvr) == null ? void 0 : _c.length)) {
|
|
561
604
|
return {
|
|
562
605
|
content: [{ type: "text", text: "No variant data available yet." }],
|
|
563
606
|
structuredContent,
|
|
@@ -565,29 +608,31 @@ function registerComponentTools(server, client) {
|
|
|
565
608
|
};
|
|
566
609
|
}
|
|
567
610
|
const text = data.cvr.map(
|
|
568
|
-
(v) =>
|
|
611
|
+
(v) => {
|
|
612
|
+
var _a2;
|
|
613
|
+
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"})`;
|
|
614
|
+
}
|
|
569
615
|
).join("\n");
|
|
570
616
|
return { content: [{ type: "text", text }], structuredContent, _meta: uiMeta("variant-performance") };
|
|
571
|
-
}
|
|
617
|
+
})
|
|
572
618
|
);
|
|
573
619
|
}
|
|
574
620
|
|
|
575
621
|
// src/tools/insights.ts
|
|
576
|
-
import { z as
|
|
577
|
-
var projectIdSchema3 = z3.string().uuid().describe("The project UUID");
|
|
622
|
+
import { z as z4 } from "zod";
|
|
578
623
|
function registerInsightTools(server, client) {
|
|
579
624
|
server.registerTool(
|
|
580
625
|
"get_insights",
|
|
581
626
|
{
|
|
582
627
|
title: "Get insights",
|
|
583
628
|
description: "Get the latest AI-generated insights: narrator observations and (Growth tier) advisor recommendations.",
|
|
584
|
-
inputSchema: { projectId:
|
|
629
|
+
inputSchema: { projectId: projectIdSchema },
|
|
585
630
|
outputSchema: {
|
|
586
|
-
status:
|
|
587
|
-
observations:
|
|
588
|
-
recommendations:
|
|
589
|
-
isStale:
|
|
590
|
-
generatedAt:
|
|
631
|
+
status: z4.enum(["ok", "empty"]).describe("Whether insights exist yet"),
|
|
632
|
+
observations: z4.array(z4.string()).describe("Narrator observations"),
|
|
633
|
+
recommendations: z4.array(z4.string()).describe("Advisor recommendations (Growth tier)"),
|
|
634
|
+
isStale: z4.boolean().describe("True when the insights are older than ~6h"),
|
|
635
|
+
generatedAt: z4.string().nullable().describe("ISO timestamp the insights were generated, or null")
|
|
591
636
|
},
|
|
592
637
|
annotations: {
|
|
593
638
|
readOnlyHint: true,
|
|
@@ -595,7 +640,8 @@ function registerInsightTools(server, client) {
|
|
|
595
640
|
openWorldHint: false
|
|
596
641
|
}
|
|
597
642
|
},
|
|
598
|
-
async ({ projectId }) => {
|
|
643
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
644
|
+
var _a, _b, _c, _d;
|
|
599
645
|
const id = encodeURIComponent(projectId);
|
|
600
646
|
const data = await client.get(`/projects/${id}/insights`);
|
|
601
647
|
if (data.status === "empty") {
|
|
@@ -610,8 +656,8 @@ function registerInsightTools(server, client) {
|
|
|
610
656
|
}
|
|
611
657
|
};
|
|
612
658
|
}
|
|
613
|
-
const observations = data.narratorBullets
|
|
614
|
-
const recommendations = data.advisorBullets
|
|
659
|
+
const observations = (_a = data.narratorBullets) != null ? _a : [];
|
|
660
|
+
const recommendations = (_b = data.advisorBullets) != null ? _b : [];
|
|
615
661
|
const lines = [];
|
|
616
662
|
if (data.isStale) lines.push("\u26A0 Insights are stale (>6h old). Consider calling refresh_insights.");
|
|
617
663
|
if (data.generatedAt) lines.push(`Generated: ${new Date(data.generatedAt).toUTCString()}`);
|
|
@@ -629,33 +675,32 @@ function registerInsightTools(server, client) {
|
|
|
629
675
|
status: "ok",
|
|
630
676
|
observations,
|
|
631
677
|
recommendations,
|
|
632
|
-
isStale: data.isStale
|
|
633
|
-
generatedAt: data.generatedAt
|
|
678
|
+
isStale: (_c = data.isStale) != null ? _c : false,
|
|
679
|
+
generatedAt: (_d = data.generatedAt) != null ? _d : null
|
|
634
680
|
}
|
|
635
681
|
};
|
|
636
|
-
}
|
|
682
|
+
})
|
|
637
683
|
);
|
|
638
684
|
}
|
|
639
685
|
|
|
640
686
|
// src/tools/personas.ts
|
|
641
|
-
import { z as
|
|
642
|
-
var projectIdSchema4 = z4.string().uuid().describe("The project UUID");
|
|
687
|
+
import { z as z5 } from "zod";
|
|
643
688
|
function registerPersonaTools(server, client) {
|
|
644
689
|
server.registerTool(
|
|
645
690
|
"get_persona_breakdown",
|
|
646
691
|
{
|
|
647
692
|
title: "Persona breakdown",
|
|
648
693
|
description: "Get the distribution of visitor persona clusters with session counts and reliability scores.",
|
|
649
|
-
inputSchema: { projectId:
|
|
694
|
+
inputSchema: { projectId: projectIdSchema },
|
|
650
695
|
_meta: uiMeta("persona-breakdown"),
|
|
651
696
|
outputSchema: {
|
|
652
|
-
totalSessions:
|
|
653
|
-
clusters:
|
|
654
|
-
|
|
655
|
-
label:
|
|
656
|
-
sessionCount:
|
|
657
|
-
sharePct:
|
|
658
|
-
reliability:
|
|
697
|
+
totalSessions: z5.number().describe("Total sessions across all clusters"),
|
|
698
|
+
clusters: z5.array(
|
|
699
|
+
z5.object({
|
|
700
|
+
label: z5.string(),
|
|
701
|
+
sessionCount: z5.number(),
|
|
702
|
+
sharePct: z5.number().describe("Share of total traffic (0-100)"),
|
|
703
|
+
reliability: z5.number().describe("Average cluster reliability (0-1)")
|
|
659
704
|
})
|
|
660
705
|
).describe("Persona clusters (empty until enough visitor data)")
|
|
661
706
|
},
|
|
@@ -665,7 +710,7 @@ function registerPersonaTools(server, client) {
|
|
|
665
710
|
openWorldHint: false
|
|
666
711
|
}
|
|
667
712
|
},
|
|
668
|
-
async ({ projectId }) => {
|
|
713
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
669
714
|
const id = encodeURIComponent(projectId);
|
|
670
715
|
const data = await client.get(`/projects/${id}/portraits`);
|
|
671
716
|
const structuredContent = {
|
|
@@ -694,33 +739,32 @@ function registerPersonaTools(server, client) {
|
|
|
694
739
|
})
|
|
695
740
|
];
|
|
696
741
|
return { content: [{ type: "text", text: lines.join("\n") }], structuredContent, _meta: uiMeta("persona-breakdown") };
|
|
697
|
-
}
|
|
742
|
+
})
|
|
698
743
|
);
|
|
699
744
|
}
|
|
700
745
|
|
|
701
746
|
// src/tools/goals.ts
|
|
702
|
-
import { z as
|
|
703
|
-
var projectIdSchema5 = z5.string().uuid().describe("The project UUID");
|
|
747
|
+
import { z as z6 } from "zod";
|
|
704
748
|
function registerGoalTools(server, client) {
|
|
705
749
|
server.registerTool(
|
|
706
750
|
"get_goal_funnel",
|
|
707
751
|
{
|
|
708
752
|
title: "Goal funnel",
|
|
709
753
|
description: "Get goal hit counts, unique-session conversion rates, and per-variant breakdown.",
|
|
710
|
-
inputSchema: { projectId:
|
|
754
|
+
inputSchema: { projectId: projectIdSchema },
|
|
711
755
|
_meta: uiMeta("goal-funnel"),
|
|
712
756
|
outputSchema: {
|
|
713
|
-
goals:
|
|
714
|
-
|
|
715
|
-
goalName:
|
|
716
|
-
hits:
|
|
717
|
-
uniqueSessions:
|
|
718
|
-
conversionRate:
|
|
719
|
-
variants:
|
|
720
|
-
|
|
721
|
-
componentId:
|
|
722
|
-
variantId:
|
|
723
|
-
completionRate:
|
|
757
|
+
goals: z6.array(
|
|
758
|
+
z6.object({
|
|
759
|
+
goalName: z6.string(),
|
|
760
|
+
hits: z6.number(),
|
|
761
|
+
uniqueSessions: z6.number(),
|
|
762
|
+
conversionRate: z6.number().describe("Unique-session conversion rate (0-1)"),
|
|
763
|
+
variants: z6.array(
|
|
764
|
+
z6.object({
|
|
765
|
+
componentId: z6.string(),
|
|
766
|
+
variantId: z6.string(),
|
|
767
|
+
completionRate: z6.number().describe("Completion rate per assigned session (0-1)")
|
|
724
768
|
})
|
|
725
769
|
).describe("Per-variant breakdown")
|
|
726
770
|
})
|
|
@@ -732,7 +776,7 @@ function registerGoalTools(server, client) {
|
|
|
732
776
|
openWorldHint: false
|
|
733
777
|
}
|
|
734
778
|
},
|
|
735
|
-
async ({ projectId }) => {
|
|
779
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
736
780
|
const id = encodeURIComponent(projectId);
|
|
737
781
|
const data = await client.get(`/projects/${id}/goals`);
|
|
738
782
|
const structuredContent = {
|
|
@@ -761,26 +805,25 @@ function registerGoalTools(server, client) {
|
|
|
761
805
|
""
|
|
762
806
|
]);
|
|
763
807
|
return { content: [{ type: "text", text: lines.join("\n").trim() }], structuredContent, _meta: uiMeta("goal-funnel") };
|
|
764
|
-
}
|
|
808
|
+
})
|
|
765
809
|
);
|
|
766
810
|
}
|
|
767
811
|
|
|
768
812
|
// src/tools/guardrails.ts
|
|
769
|
-
import { z as
|
|
770
|
-
var projectIdSchema6 = z6.string().uuid().describe("The project UUID");
|
|
813
|
+
import { z as z7 } from "zod";
|
|
771
814
|
function registerGuardrailTools(server, client) {
|
|
772
815
|
server.registerTool(
|
|
773
816
|
"list_guardrail_events",
|
|
774
817
|
{
|
|
775
818
|
title: "List guardrail events",
|
|
776
819
|
description: "List variants currently paused by the guardrail in the last 24 hours.",
|
|
777
|
-
inputSchema: { projectId:
|
|
820
|
+
inputSchema: { projectId: projectIdSchema },
|
|
778
821
|
outputSchema: {
|
|
779
|
-
events:
|
|
780
|
-
|
|
781
|
-
componentId:
|
|
782
|
-
variantIds:
|
|
783
|
-
pausedAt:
|
|
822
|
+
events: z7.array(
|
|
823
|
+
z7.object({
|
|
824
|
+
componentId: z7.string(),
|
|
825
|
+
variantIds: z7.array(z7.string()).describe("Variants paused by the guardrail"),
|
|
826
|
+
pausedAt: z7.string().nullable().describe("ISO timestamp the pause fired, or null")
|
|
784
827
|
})
|
|
785
828
|
).describe("Guardrail events in the last 24h (empty if none)")
|
|
786
829
|
},
|
|
@@ -790,7 +833,7 @@ function registerGuardrailTools(server, client) {
|
|
|
790
833
|
openWorldHint: false
|
|
791
834
|
}
|
|
792
835
|
},
|
|
793
|
-
async ({ projectId }) => {
|
|
836
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
794
837
|
const id = encodeURIComponent(projectId);
|
|
795
838
|
const data = await client.get(`/projects/${id}/guardrail-events`);
|
|
796
839
|
const structuredContent = {
|
|
@@ -810,28 +853,27 @@ function registerGuardrailTools(server, client) {
|
|
|
810
853
|
(e) => `- ${e.componentId}: variants [${e.variantIds.join(", ")}] paused${e.pausedAt ? ` at ${e.pausedAt}` : ""}`
|
|
811
854
|
);
|
|
812
855
|
return { content: [{ type: "text", text: lines.join("\n") }], structuredContent };
|
|
813
|
-
}
|
|
856
|
+
})
|
|
814
857
|
);
|
|
815
858
|
}
|
|
816
859
|
|
|
817
860
|
// src/tools/layout.ts
|
|
818
|
-
import { z as
|
|
819
|
-
var projectIdSchema7 = z7.string().uuid().describe("The project UUID");
|
|
861
|
+
import { z as z8 } from "zod";
|
|
820
862
|
function registerLayoutTools(server, client) {
|
|
821
863
|
server.registerTool(
|
|
822
864
|
"get_layout_stats",
|
|
823
865
|
{
|
|
824
866
|
title: "Layout stats",
|
|
825
867
|
description: "Get per-persona section layout rankings and bandit reward weights.",
|
|
826
|
-
inputSchema: { projectId:
|
|
868
|
+
inputSchema: { projectId: projectIdSchema },
|
|
827
869
|
_meta: uiMeta("layout-stats"),
|
|
828
870
|
outputSchema: {
|
|
829
|
-
layouts:
|
|
830
|
-
|
|
831
|
-
persona:
|
|
832
|
-
layoutOrder:
|
|
833
|
-
pulls:
|
|
834
|
-
avgReward:
|
|
871
|
+
layouts: z8.array(
|
|
872
|
+
z8.object({
|
|
873
|
+
persona: z8.string(),
|
|
874
|
+
layoutOrder: z8.array(z8.string()).describe("Ranked section order for this persona"),
|
|
875
|
+
pulls: z8.number().describe("Number of times this arm was served"),
|
|
876
|
+
avgReward: z8.number().describe("Average bandit reward weight")
|
|
835
877
|
})
|
|
836
878
|
).describe("Per-persona layout rankings (empty until enough sessions)")
|
|
837
879
|
},
|
|
@@ -841,7 +883,7 @@ function registerLayoutTools(server, client) {
|
|
|
841
883
|
openWorldHint: false
|
|
842
884
|
}
|
|
843
885
|
},
|
|
844
|
-
async ({ projectId }) => {
|
|
886
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
845
887
|
const id = encodeURIComponent(projectId);
|
|
846
888
|
const stats = await client.get(`/projects/${id}/layout-stats`);
|
|
847
889
|
const structuredContent = {
|
|
@@ -863,13 +905,12 @@ function registerLayoutTools(server, client) {
|
|
|
863
905
|
(s) => `- ${s.persona}: [${s.layoutOrder.join(" \u2192 ")}] (avg reward: ${s.avgReward.toFixed(2)}, ${s.pulls} pulls)`
|
|
864
906
|
).join("\n");
|
|
865
907
|
return { content: [{ type: "text", text }], structuredContent, _meta: uiMeta("layout-stats") };
|
|
866
|
-
}
|
|
908
|
+
})
|
|
867
909
|
);
|
|
868
910
|
}
|
|
869
911
|
|
|
870
912
|
// src/tools/variants.ts
|
|
871
|
-
import { z as
|
|
872
|
-
var projectIdSchema8 = z8.string().uuid().describe("The project UUID");
|
|
913
|
+
import { z as z9 } from "zod";
|
|
873
914
|
function registerVariantWriteTools(server, client) {
|
|
874
915
|
server.registerTool(
|
|
875
916
|
"create_variant",
|
|
@@ -877,17 +918,17 @@ function registerVariantWriteTools(server, client) {
|
|
|
877
918
|
title: "Create managed variant",
|
|
878
919
|
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).",
|
|
879
920
|
inputSchema: {
|
|
880
|
-
projectId:
|
|
881
|
-
componentId:
|
|
882
|
-
displayName:
|
|
883
|
-
content:
|
|
921
|
+
projectId: projectIdSchema,
|
|
922
|
+
componentId: z9.string().describe("The component ID to add a variant to"),
|
|
923
|
+
displayName: z9.string().describe("Human-readable name for the new variant"),
|
|
924
|
+
content: z9.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.")
|
|
884
925
|
},
|
|
885
926
|
outputSchema: {
|
|
886
|
-
variantId:
|
|
887
|
-
displayName:
|
|
888
|
-
componentId:
|
|
889
|
-
state:
|
|
890
|
-
hasContent:
|
|
927
|
+
variantId: z9.string().describe("The new variant ID"),
|
|
928
|
+
displayName: z9.string(),
|
|
929
|
+
componentId: z9.string(),
|
|
930
|
+
state: z9.literal("draft").describe("New managed variants start in draft state"),
|
|
931
|
+
hasContent: z9.boolean().describe("Whether text content was provided at creation")
|
|
891
932
|
},
|
|
892
933
|
annotations: {
|
|
893
934
|
readOnlyHint: false,
|
|
@@ -896,7 +937,7 @@ function registerVariantWriteTools(server, client) {
|
|
|
896
937
|
openWorldHint: false
|
|
897
938
|
}
|
|
898
939
|
},
|
|
899
|
-
async ({ projectId, componentId, displayName, content }) => {
|
|
940
|
+
withApiErrorGuidance(async ({ projectId, componentId, displayName, content }) => {
|
|
900
941
|
const id = encodeURIComponent(projectId);
|
|
901
942
|
const result = await client.post(
|
|
902
943
|
`/projects/${id}/variants`,
|
|
@@ -916,7 +957,7 @@ function registerVariantWriteTools(server, client) {
|
|
|
916
957
|
hasContent: Boolean(content)
|
|
917
958
|
}
|
|
918
959
|
};
|
|
919
|
-
}
|
|
960
|
+
})
|
|
920
961
|
);
|
|
921
962
|
server.registerTool(
|
|
922
963
|
"pause_variant",
|
|
@@ -924,14 +965,14 @@ function registerVariantWriteTools(server, client) {
|
|
|
924
965
|
title: "Pause variant",
|
|
925
966
|
description: "Pause a variant, stopping traffic from being assigned to it.",
|
|
926
967
|
inputSchema: {
|
|
927
|
-
projectId:
|
|
928
|
-
componentId:
|
|
929
|
-
variantId:
|
|
968
|
+
projectId: projectIdSchema,
|
|
969
|
+
componentId: z9.string().describe("The component ID"),
|
|
970
|
+
variantId: z9.string().describe("The variant ID to pause")
|
|
930
971
|
},
|
|
931
972
|
outputSchema: {
|
|
932
|
-
variantId:
|
|
933
|
-
componentId:
|
|
934
|
-
paused:
|
|
973
|
+
variantId: z9.string(),
|
|
974
|
+
componentId: z9.string(),
|
|
975
|
+
paused: z9.literal(true).describe("The variant is now paused")
|
|
935
976
|
},
|
|
936
977
|
annotations: {
|
|
937
978
|
readOnlyHint: false,
|
|
@@ -940,7 +981,7 @@ function registerVariantWriteTools(server, client) {
|
|
|
940
981
|
openWorldHint: false
|
|
941
982
|
}
|
|
942
983
|
},
|
|
943
|
-
async ({ projectId, componentId, variantId }) => {
|
|
984
|
+
withApiErrorGuidance(async ({ projectId, componentId, variantId }) => {
|
|
944
985
|
const id = encodeURIComponent(projectId);
|
|
945
986
|
await client.post(`/projects/${id}/variants/pause`, { componentId, variantId });
|
|
946
987
|
return {
|
|
@@ -950,17 +991,17 @@ function registerVariantWriteTools(server, client) {
|
|
|
950
991
|
}],
|
|
951
992
|
structuredContent: { variantId, componentId, paused: true }
|
|
952
993
|
};
|
|
953
|
-
}
|
|
994
|
+
})
|
|
954
995
|
);
|
|
955
996
|
server.registerTool(
|
|
956
997
|
"refresh_insights",
|
|
957
998
|
{
|
|
958
999
|
title: "Refresh insights",
|
|
959
1000
|
description: "Trigger fresh AI insight generation for a project. Returns immediately; use get_insights in ~15 seconds to see results.",
|
|
960
|
-
inputSchema: { projectId:
|
|
1001
|
+
inputSchema: { projectId: projectIdSchema },
|
|
961
1002
|
outputSchema: {
|
|
962
|
-
projectId:
|
|
963
|
-
status:
|
|
1003
|
+
projectId: z9.string(),
|
|
1004
|
+
status: z9.literal("generating").describe("Generation has been triggered")
|
|
964
1005
|
},
|
|
965
1006
|
annotations: {
|
|
966
1007
|
readOnlyHint: false,
|
|
@@ -969,7 +1010,7 @@ function registerVariantWriteTools(server, client) {
|
|
|
969
1010
|
openWorldHint: false
|
|
970
1011
|
}
|
|
971
1012
|
},
|
|
972
|
-
async ({ projectId }) => {
|
|
1013
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
973
1014
|
const id = encodeURIComponent(projectId);
|
|
974
1015
|
await client.post(`/projects/${id}/insights/refresh`);
|
|
975
1016
|
return {
|
|
@@ -979,13 +1020,12 @@ function registerVariantWriteTools(server, client) {
|
|
|
979
1020
|
}],
|
|
980
1021
|
structuredContent: { projectId, status: "generating" }
|
|
981
1022
|
};
|
|
982
|
-
}
|
|
1023
|
+
})
|
|
983
1024
|
);
|
|
984
1025
|
}
|
|
985
1026
|
|
|
986
1027
|
// src/tools/variant-brief.ts
|
|
987
|
-
import { z as
|
|
988
|
-
var projectIdSchema9 = z9.string().uuid().describe("The project UUID");
|
|
1028
|
+
import { z as z10 } from "zod";
|
|
989
1029
|
var GOAL_TARGET = 500;
|
|
990
1030
|
var BEST_PRACTICE_PRIORS = {
|
|
991
1031
|
ecommerce: [
|
|
@@ -1023,11 +1063,12 @@ var GENERIC_PRIORS = [
|
|
|
1023
1063
|
"Add one credible proof point near the action."
|
|
1024
1064
|
];
|
|
1025
1065
|
function priorsFor(contextType) {
|
|
1026
|
-
|
|
1066
|
+
var _a;
|
|
1067
|
+
return (_a = BEST_PRACTICE_PRIORS[contextType]) != null ? _a : GENERIC_PRIORS;
|
|
1027
1068
|
}
|
|
1028
1069
|
function computeDataState(impressions, insights, avgReliability) {
|
|
1029
1070
|
if (impressions === 0) return "empty";
|
|
1030
|
-
const insightsReady = insights
|
|
1071
|
+
const insightsReady = (insights == null ? void 0 : insights.status) === "ok" && !insights.isStale;
|
|
1031
1072
|
const reliable = avgReliability === null || avgReliability >= 0.3;
|
|
1032
1073
|
if (impressions < GOAL_TARGET || !insightsReady || !reliable) return "collecting";
|
|
1033
1074
|
return "sufficient";
|
|
@@ -1045,7 +1086,7 @@ function guidanceFor(dataState, contextType) {
|
|
|
1045
1086
|
async function settled(p) {
|
|
1046
1087
|
try {
|
|
1047
1088
|
return await p;
|
|
1048
|
-
} catch {
|
|
1089
|
+
} catch (e) {
|
|
1049
1090
|
return null;
|
|
1050
1091
|
}
|
|
1051
1092
|
}
|
|
@@ -1056,16 +1097,16 @@ function registerVariantBriefTools(server, client) {
|
|
|
1056
1097
|
title: "Variant brief",
|
|
1057
1098
|
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.",
|
|
1058
1099
|
inputSchema: {
|
|
1059
|
-
projectId:
|
|
1060
|
-
componentId:
|
|
1100
|
+
projectId: projectIdSchema,
|
|
1101
|
+
componentId: z10.string().describe('The component ID to write a new variant for (matches <Adaptive id="...">).')
|
|
1061
1102
|
},
|
|
1062
1103
|
outputSchema: {
|
|
1063
|
-
componentId:
|
|
1064
|
-
contextType:
|
|
1065
|
-
dataState:
|
|
1066
|
-
existingVariantIds:
|
|
1067
|
-
priors:
|
|
1068
|
-
markdown:
|
|
1104
|
+
componentId: z10.string(),
|
|
1105
|
+
contextType: z10.string().describe("The project's context type (or 'unknown')"),
|
|
1106
|
+
dataState: z10.enum(["sufficient", "collecting", "empty"]).describe("Data-sufficiency assessment"),
|
|
1107
|
+
existingVariantIds: z10.array(z10.string()).describe("Variant IDs already in use (do not reuse)"),
|
|
1108
|
+
priors: z10.array(z10.string()).describe("Best-practice priors applied for this context type"),
|
|
1109
|
+
markdown: z10.string().describe("The full variant brief in Markdown")
|
|
1069
1110
|
},
|
|
1070
1111
|
annotations: {
|
|
1071
1112
|
readOnlyHint: true,
|
|
@@ -1073,7 +1114,8 @@ function registerVariantBriefTools(server, client) {
|
|
|
1073
1114
|
openWorldHint: false
|
|
1074
1115
|
}
|
|
1075
1116
|
},
|
|
1076
|
-
async ({ projectId, componentId }) => {
|
|
1117
|
+
withApiErrorGuidance(async ({ projectId, componentId }) => {
|
|
1118
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
1077
1119
|
const id = encodeURIComponent(projectId);
|
|
1078
1120
|
const [projects, componentsEnvelope, trends, portraits, insights] = await Promise.all([
|
|
1079
1121
|
settled(client.get("/projects")),
|
|
@@ -1083,19 +1125,19 @@ function registerVariantBriefTools(server, client) {
|
|
|
1083
1125
|
settled(client.get(`/projects/${id}/portraits`)),
|
|
1084
1126
|
settled(client.get(`/projects/${id}/insights`))
|
|
1085
1127
|
]);
|
|
1086
|
-
const project = projects
|
|
1087
|
-
const contextType = project
|
|
1088
|
-
const components = componentsEnvelope
|
|
1089
|
-
const component = components.find((c) => c.component_id === componentId)
|
|
1090
|
-
const impressions = component
|
|
1091
|
-
const conversions = component
|
|
1128
|
+
const project = (_a = projects == null ? void 0 : projects.find((p) => p.id === projectId)) != null ? _a : null;
|
|
1129
|
+
const contextType = (_b = project == null ? void 0 : project.context_type) != null ? _b : "unknown";
|
|
1130
|
+
const components = (_c = componentsEnvelope == null ? void 0 : componentsEnvelope.components) != null ? _c : [];
|
|
1131
|
+
const component = (_d = components.find((c) => c.component_id === componentId)) != null ? _d : null;
|
|
1132
|
+
const impressions = (_e = component == null ? void 0 : component.total_impressions) != null ? _e : 0;
|
|
1133
|
+
const conversions = (_f = component == null ? void 0 : component.total_conversions) != null ? _f : 0;
|
|
1092
1134
|
const componentCvr = impressions > 0 ? conversions / impressions * 100 : 0;
|
|
1093
|
-
const existingVariantIds = component
|
|
1135
|
+
const existingVariantIds = (_g = component == null ? void 0 : component.variants.map((v) => v.variant_id)) != null ? _g : [];
|
|
1094
1136
|
const variantIdSet = new Set(existingVariantIds);
|
|
1095
|
-
const momentumMap = new Map((trends
|
|
1096
|
-
const variantPerf = (trends
|
|
1097
|
-
const clusters = portraits
|
|
1098
|
-
const totalSessions = portraits
|
|
1137
|
+
const momentumMap = new Map(((_h = trends == null ? void 0 : trends.momentum) != null ? _h : []).map((m) => [m.variantId, m.direction]));
|
|
1138
|
+
const variantPerf = ((_i = trends == null ? void 0 : trends.cvr) != null ? _i : []).filter((v) => variantIdSet.has(v.variantId));
|
|
1139
|
+
const clusters = (_j = portraits == null ? void 0 : portraits.clusters) != null ? _j : [];
|
|
1140
|
+
const totalSessions = (_k = portraits == null ? void 0 : portraits.totalSessions) != null ? _k : 0;
|
|
1099
1141
|
const avgReliability = clusters.length ? clusters.reduce((s, c) => s + c.avgReliability, 0) / clusters.length : null;
|
|
1100
1142
|
const dataState = computeDataState(impressions, insights, avgReliability);
|
|
1101
1143
|
const lines = [];
|
|
@@ -1120,7 +1162,7 @@ function registerVariantBriefTools(server, client) {
|
|
|
1120
1162
|
lines.push("Current variant performance (7d vs prior 7d):");
|
|
1121
1163
|
for (const v of variantPerf) {
|
|
1122
1164
|
lines.push(
|
|
1123
|
-
`- ${v.variantId}: ${(v.currentCvr * 100).toFixed(2)}% CVR (${v.deltaPp > 0 ? "+" : ""}${v.deltaPp.toFixed(1)} pp, ${momentumMap.get(v.variantId)
|
|
1165
|
+
`- ${v.variantId}: ${(v.currentCvr * 100).toFixed(2)}% CVR (${v.deltaPp > 0 ? "+" : ""}${v.deltaPp.toFixed(1)} pp, ${(_l = momentumMap.get(v.variantId)) != null ? _l : "stable"})`
|
|
1124
1166
|
);
|
|
1125
1167
|
}
|
|
1126
1168
|
lines.push("");
|
|
@@ -1135,8 +1177,8 @@ function registerVariantBriefTools(server, client) {
|
|
|
1135
1177
|
}
|
|
1136
1178
|
if (insights && insights.status === "ok") {
|
|
1137
1179
|
if (insights.isStale) lines.push("\u26A0 Insights are stale (>6h). Consider refresh_insights for a fresher read.");
|
|
1138
|
-
const narrator = insights.narratorBullets
|
|
1139
|
-
const advisor = insights.advisorBullets
|
|
1180
|
+
const narrator = (_m = insights.narratorBullets) != null ? _m : [];
|
|
1181
|
+
const advisor = (_n = insights.advisorBullets) != null ? _n : [];
|
|
1140
1182
|
if (narrator.length) {
|
|
1141
1183
|
lines.push("Insights \u2014 observations:");
|
|
1142
1184
|
narrator.forEach((b) => lines.push(`- ${b}`));
|
|
@@ -1176,17 +1218,16 @@ function registerVariantBriefTools(server, client) {
|
|
|
1176
1218
|
markdown
|
|
1177
1219
|
}
|
|
1178
1220
|
};
|
|
1179
|
-
}
|
|
1221
|
+
})
|
|
1180
1222
|
);
|
|
1181
1223
|
}
|
|
1182
1224
|
|
|
1183
1225
|
// src/tools/test-brief.ts
|
|
1184
|
-
import { z as
|
|
1185
|
-
var projectIdSchema10 = z10.string().uuid().describe("The project UUID");
|
|
1226
|
+
import { z as z11 } from "zod";
|
|
1186
1227
|
async function settled2(p) {
|
|
1187
1228
|
try {
|
|
1188
1229
|
return await p;
|
|
1189
|
-
} catch {
|
|
1230
|
+
} catch (e) {
|
|
1190
1231
|
return null;
|
|
1191
1232
|
}
|
|
1192
1233
|
}
|
|
@@ -1197,14 +1238,14 @@ function registerTestBriefTools(server, client) {
|
|
|
1197
1238
|
title: "Test brief",
|
|
1198
1239
|
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).",
|
|
1199
1240
|
inputSchema: {
|
|
1200
|
-
projectId:
|
|
1201
|
-
componentId:
|
|
1241
|
+
projectId: projectIdSchema,
|
|
1242
|
+
componentId: z11.string().describe('The component ID to write a test for (matches <Adaptive id="...">).')
|
|
1202
1243
|
},
|
|
1203
1244
|
outputSchema: {
|
|
1204
|
-
componentId:
|
|
1205
|
-
forcedVariantId:
|
|
1206
|
-
goalName:
|
|
1207
|
-
markdown:
|
|
1245
|
+
componentId: z11.string(),
|
|
1246
|
+
forcedVariantId: z11.string().describe("The non-control variant the example forces"),
|
|
1247
|
+
goalName: z11.string().describe("The goal the example asserts fires"),
|
|
1248
|
+
markdown: z11.string().describe("The full test brief in Markdown")
|
|
1208
1249
|
},
|
|
1209
1250
|
annotations: {
|
|
1210
1251
|
readOnlyHint: true,
|
|
@@ -1212,20 +1253,21 @@ function registerTestBriefTools(server, client) {
|
|
|
1212
1253
|
openWorldHint: false
|
|
1213
1254
|
}
|
|
1214
1255
|
},
|
|
1215
|
-
async ({ projectId, componentId }) => {
|
|
1256
|
+
withApiErrorGuidance(async ({ projectId, componentId }) => {
|
|
1257
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1216
1258
|
const id = encodeURIComponent(projectId);
|
|
1217
1259
|
const [componentsEnvelope, goalsRes] = await Promise.all([
|
|
1218
1260
|
// mgmt API returns a paginated envelope: { components, total, page, limit }.
|
|
1219
1261
|
settled2(client.get(`/projects/${id}/components`)),
|
|
1220
1262
|
settled2(client.get(`/projects/${id}/goals`))
|
|
1221
1263
|
]);
|
|
1222
|
-
const components = componentsEnvelope
|
|
1223
|
-
const component = components.find((c) => c.component_id === componentId)
|
|
1224
|
-
const variantIds = component
|
|
1225
|
-
const goals = Array.isArray(goalsRes) ? goalsRes : goalsRes
|
|
1226
|
-
const goalName = goals[0]
|
|
1227
|
-
const controlId = variantIds[0]
|
|
1228
|
-
const forcedId = variantIds.find((v) => v !== controlId)
|
|
1264
|
+
const components = (_a = componentsEnvelope == null ? void 0 : componentsEnvelope.components) != null ? _a : [];
|
|
1265
|
+
const component = (_b = components.find((c) => c.component_id === componentId)) != null ? _b : null;
|
|
1266
|
+
const variantIds = (_c = component == null ? void 0 : component.variants.map((v) => v.variant_id)) != null ? _c : [];
|
|
1267
|
+
const goals = Array.isArray(goalsRes) ? goalsRes : (_d = goalsRes == null ? void 0 : goalsRes.goals) != null ? _d : [];
|
|
1268
|
+
const goalName = (_f = (_e = goals[0]) == null ? void 0 : _e.goalName) != null ? _f : "signup";
|
|
1269
|
+
const controlId = (_g = variantIds[0]) != null ? _g : "control";
|
|
1270
|
+
const forcedId = (_h = variantIds.find((v) => v !== controlId)) != null ? _h : "variant_b";
|
|
1229
1271
|
const lines = [];
|
|
1230
1272
|
lines.push(`# Test brief \u2014 ${componentId}`);
|
|
1231
1273
|
lines.push("");
|
|
@@ -1281,12 +1323,12 @@ function registerTestBriefTools(server, client) {
|
|
|
1281
1323
|
content: [{ type: "text", text: markdown }],
|
|
1282
1324
|
structuredContent: { componentId, forcedVariantId: forcedId, goalName, markdown }
|
|
1283
1325
|
};
|
|
1284
|
-
}
|
|
1326
|
+
})
|
|
1285
1327
|
);
|
|
1286
1328
|
}
|
|
1287
1329
|
|
|
1288
1330
|
// src/tools/integration-guide.ts
|
|
1289
|
-
import { z as
|
|
1331
|
+
import { z as z12 } from "zod";
|
|
1290
1332
|
var GUIDE = `# SentientUI integration guide \u2014 the adaptive ladder
|
|
1291
1333
|
|
|
1292
1334
|
SentientUI adapts a site per visitor type (personas: buyer, researcher, deal_seeker, browser,
|
|
@@ -1361,7 +1403,7 @@ function registerIntegrationGuideTools(server) {
|
|
|
1361
1403
|
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.",
|
|
1362
1404
|
inputSchema: {},
|
|
1363
1405
|
outputSchema: {
|
|
1364
|
-
guide:
|
|
1406
|
+
guide: z12.string().describe("The full integration guide in Markdown")
|
|
1365
1407
|
},
|
|
1366
1408
|
annotations: {
|
|
1367
1409
|
readOnlyHint: true,
|
|
@@ -1377,7 +1419,8 @@ function registerIntegrationGuideTools(server) {
|
|
|
1377
1419
|
}
|
|
1378
1420
|
|
|
1379
1421
|
// src/server.ts
|
|
1380
|
-
var
|
|
1422
|
+
var import_meta = {};
|
|
1423
|
+
var { version: PKG_VERSION } = createRequire(import_meta.url)("../package.json");
|
|
1381
1424
|
function createMcpServer(client) {
|
|
1382
1425
|
const server = new McpServer(
|
|
1383
1426
|
{
|