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