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