@sentientui/mcp 0.17.6 → 0.18.0
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/AGENTS.md +3 -0
- package/README.md +3 -0
- package/dist/{chunk-FBRHD7R7.js → chunk-PX6RCSB2.js} +164 -5
- package/dist/index.cjs +164 -5
- package/dist/index.js +1 -1
- package/dist/lib.cjs +164 -5
- package/dist/lib.js +1 -1
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -43,6 +43,9 @@ Server keys start with `sk_` — required for the management API. Public keys (`
|
|
|
43
43
|
| `get_variant_brief` | **Start here to optimize a component.** Insight-driven brief for writing a new CODE-NATIVE variant: performance, audience, insights, data-sufficiency + best-practice fallback, and code instructions |
|
|
44
44
|
| `create_variant` | Creates a no-code **managed text** DRAFT — user must activate in dashboard. Fallback only; not for code-native variants |
|
|
45
45
|
| `pause_variant` | Stops traffic; no MCP resume |
|
|
46
|
+
| `get_cell_matrix` | "Who sees what": per-(visitor type, region) generation state + traffic share. A missing cell = they see the original |
|
|
47
|
+
| `get_cell_detail` | One cell's generated option, its stored rationale, and a plain-language performance verdict |
|
|
48
|
+
| `generate_cell` | Queues async AI generation for one cell (also retries failed ones). Paid plan; check `get_cell_matrix` for progress |
|
|
46
49
|
|
|
47
50
|
## Canonical Workflows
|
|
48
51
|
|
package/README.md
CHANGED
|
@@ -101,6 +101,9 @@ A sandboxed demo token is provisioned automatically — 10 calls/month, read-onl
|
|
|
101
101
|
| `get_variant_brief` | Insight-driven brief for writing a new **code-native** variant: performance, audience, insights, data-sufficiency (with a best-practice fallback when there's no data yet), and step-by-step code instructions |
|
|
102
102
|
| `create_variant` | Create a no-code (managed) text variant (Starter+) — fallback for text-only variants without a code change |
|
|
103
103
|
| `pause_variant` | Pause a variant to stop traffic assignment |
|
|
104
|
+
| `get_cell_matrix` | The "Who sees what" matrix: which visitor types have an AI-generated version live in each personalizable region, with traffic share and auto-fill status |
|
|
105
|
+
| `get_cell_detail` | One cell's story: the generated option, why it was written (stored rationale), and how it performs vs the original |
|
|
106
|
+
| `generate_cell` | Queue AI generation of a version for one (visitor type, region) cell — brand-locked, live in under a minute (paid plan) |
|
|
104
107
|
|
|
105
108
|
> **Code-native vs no-code variants.** Variants you declare in your app (`<Adaptive variants={{…}}>`) register automatically the first time the SDK requests an assignment after you deploy — they go live immediately and need **no** `create_variant` call. Use `create_variant` only for **no-code** variants whose content is stored in SentientUI and rendered without a code change; these start as drafts you activate from the dashboard.
|
|
106
109
|
>
|
|
@@ -44,10 +44,9 @@ var ApiClient = class {
|
|
|
44
44
|
var _a, _b;
|
|
45
45
|
const res = await fetch(`${this.baseUrl}/v1/mgmt${path}`, {
|
|
46
46
|
method,
|
|
47
|
-
headers: {
|
|
48
|
-
authorization: `Bearer ${this.apiKey}
|
|
49
|
-
|
|
50
|
-
},
|
|
47
|
+
headers: __spreadValues({
|
|
48
|
+
authorization: `Bearer ${this.apiKey}`
|
|
49
|
+
}, body !== void 0 ? { "content-type": "application/json" } : {}),
|
|
51
50
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
52
51
|
});
|
|
53
52
|
if (res.ok) return res.json();
|
|
@@ -1857,6 +1856,15 @@ useLayoutOrder(). Non-React sites: sections: ['#hero', '#pricing', '#faq'] (CSS
|
|
|
1857
1856
|
the theme's natural order) in window.sentient \u2014 the snippet reorders those elements within
|
|
1858
1857
|
their shared parent, and applies nothing if any selector drifts.
|
|
1859
1858
|
|
|
1859
|
+
## No-code install order (snippet)
|
|
1860
|
+
|
|
1861
|
+
Three tags in <head>, in this order: the window.sentient config, the inline pre-paint script,
|
|
1862
|
+
then the deferred loader. The middle tag is optional but recommended \u2014 the loader is deferred,
|
|
1863
|
+
so on a return visit the page can paint its original state before the loader runs; the inline
|
|
1864
|
+
script applies the last served decision from the visitor's own device first, with no network
|
|
1865
|
+
call and no cloak. Get its exact bytes from the dashboard's Install page, or from
|
|
1866
|
+
renderSnippetPrePaintScript() in '@sentientui/snippet/install'.
|
|
1867
|
+
|
|
1860
1868
|
## Testing the integration
|
|
1861
1869
|
|
|
1862
1870
|
Use '@sentientui/react/testing': renderWithSentient(ui, { variants, slots, persona }) forces
|
|
@@ -2018,8 +2026,158 @@ function registerAgentTrafficTools(server, client) {
|
|
|
2018
2026
|
);
|
|
2019
2027
|
}
|
|
2020
2028
|
|
|
2029
|
+
// src/tools/cells.ts
|
|
2030
|
+
import { z as z15 } from "zod";
|
|
2031
|
+
var personaOutput = z15.object({
|
|
2032
|
+
key: z15.string(),
|
|
2033
|
+
displayName: z15.string(),
|
|
2034
|
+
description: z15.string().nullable(),
|
|
2035
|
+
share: z15.number().describe("Fraction of 30-day decided sessions")
|
|
2036
|
+
});
|
|
2037
|
+
var cellOutput = z15.object({
|
|
2038
|
+
slotId: z15.string(),
|
|
2039
|
+
persona: z15.string(),
|
|
2040
|
+
status: z15.string().describe("pending | generating | live | error | rejected"),
|
|
2041
|
+
requestedBy: z15.string(),
|
|
2042
|
+
armId: z15.string().nullable(),
|
|
2043
|
+
rationale: z15.string().nullable(),
|
|
2044
|
+
error: z15.string().nullable(),
|
|
2045
|
+
updatedAt: z15.string(),
|
|
2046
|
+
performance: z15.object({
|
|
2047
|
+
shown: z15.number(),
|
|
2048
|
+
conversions: z15.number(),
|
|
2049
|
+
deltaPct: z15.number().nullable().describe("Signed % vs the original conversion rate"),
|
|
2050
|
+
tier: z15.string().describe("early | no_baseline | winning | losing | flat")
|
|
2051
|
+
}).optional().describe("Present on live cells: the at-a-glance readout")
|
|
2052
|
+
});
|
|
2053
|
+
function registerCellTools(server, client) {
|
|
2054
|
+
server.registerTool(
|
|
2055
|
+
"get_cell_matrix",
|
|
2056
|
+
{
|
|
2057
|
+
title: "Persona \xD7 slot cell matrix",
|
|
2058
|
+
description: 'The "Who sees what" matrix: which visitor types (personas) have an AI-generated version live in each personalizable region (slot), with per-persona traffic share and the first-miss auto-fill setting. A cell with no row means that visitor type sees the original.' + UNTRUSTED_FIELDS_NOTE,
|
|
2059
|
+
inputSchema: { projectId: projectIdSchema },
|
|
2060
|
+
outputSchema: {
|
|
2061
|
+
slots: z15.array(z15.object({ slotId: z15.string(), displayName: z15.string(), supportsForms: z15.boolean() })),
|
|
2062
|
+
personas: z15.array(personaOutput),
|
|
2063
|
+
cells: z15.array(cellOutput),
|
|
2064
|
+
autoFill: z15.boolean(),
|
|
2065
|
+
personaSource: z15.string().describe("Active persona-set source: default | declared | discovered"),
|
|
2066
|
+
undecidedShare: z15.number()
|
|
2067
|
+
},
|
|
2068
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
|
|
2069
|
+
},
|
|
2070
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
2071
|
+
const id = encodeURIComponent(projectId);
|
|
2072
|
+
const data = await client.get(`/projects/${id}/cells`);
|
|
2073
|
+
const byCell = new Map(data.cells.map((c) => [`${c.slotId}::${c.persona}`, c]));
|
|
2074
|
+
const lines = [
|
|
2075
|
+
`Slots: ${data.slots.map((s) => untrusted(s.displayName)).join(", ") || "(none published)"}`,
|
|
2076
|
+
`Auto-fill on first miss: ${data.autoFill ? "ON" : "off"} \xB7 persona source: ${data.personaSource}`
|
|
2077
|
+
];
|
|
2078
|
+
for (const p of data.personas) {
|
|
2079
|
+
const states = data.slots.map((s) => {
|
|
2080
|
+
const c = byCell.get(`${s.slotId}::${p.key}`);
|
|
2081
|
+
return `${untrusted(s.displayName, 40)}: ${c ? c.status : "original"}`;
|
|
2082
|
+
});
|
|
2083
|
+
lines.push(`- ${untrusted(p.displayName)} (${Math.round(p.share * 100)}% of visitors) \u2014 ${states.join(" \xB7 ")}`);
|
|
2084
|
+
}
|
|
2085
|
+
lines.push(`Undecided visitors (${Math.round(data.undecidedShare * 100)}%) always see the original.`);
|
|
2086
|
+
return {
|
|
2087
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
2088
|
+
structuredContent: data
|
|
2089
|
+
};
|
|
2090
|
+
})
|
|
2091
|
+
);
|
|
2092
|
+
server.registerTool(
|
|
2093
|
+
"get_cell_detail",
|
|
2094
|
+
{
|
|
2095
|
+
title: "Cell story",
|
|
2096
|
+
description: "One cell's full story: the generated option a visitor type sees in a slot, why it was written (the stored generation rationale), and how it is performing vs the original (deterministic plain-language verdict)." + UNTRUSTED_FIELDS_NOTE,
|
|
2097
|
+
inputSchema: {
|
|
2098
|
+
projectId: projectIdSchema,
|
|
2099
|
+
slotId: z15.string().min(1).max(128).describe("The slot id (a matrix column)"),
|
|
2100
|
+
persona: z15.string().min(1).max(64).describe("The persona key (a matrix row)")
|
|
2101
|
+
},
|
|
2102
|
+
outputSchema: {
|
|
2103
|
+
cell: cellOutput.nullable(),
|
|
2104
|
+
arm: z15.object({
|
|
2105
|
+
id: z15.string(),
|
|
2106
|
+
displayName: z15.string().nullable(),
|
|
2107
|
+
content: z15.string().nullable(),
|
|
2108
|
+
blocks: z15.unknown().nullable()
|
|
2109
|
+
}).nullable(),
|
|
2110
|
+
performance: z15.object({
|
|
2111
|
+
shown: z15.number(),
|
|
2112
|
+
conversions: z15.number(),
|
|
2113
|
+
conversionRate: z15.number(),
|
|
2114
|
+
baselineShown: z15.number(),
|
|
2115
|
+
baselineConversions: z15.number(),
|
|
2116
|
+
baselineConversionRate: z15.number()
|
|
2117
|
+
}).nullable(),
|
|
2118
|
+
verdict: z15.string()
|
|
2119
|
+
},
|
|
2120
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
|
|
2121
|
+
},
|
|
2122
|
+
withApiErrorGuidance(async ({ projectId, slotId, persona }) => {
|
|
2123
|
+
var _a, _b;
|
|
2124
|
+
const id = encodeURIComponent(projectId);
|
|
2125
|
+
const data = await client.get(`/projects/${id}/cells/detail?slotId=${encodeURIComponent(slotId)}&persona=${encodeURIComponent(persona)}`);
|
|
2126
|
+
const lines = [];
|
|
2127
|
+
if (data.arm) {
|
|
2128
|
+
lines.push(
|
|
2129
|
+
data.arm.content !== null ? `They see: "${untrusted(data.arm.content, 300)}"` : `They see a designed layout (option ${untrusted(data.arm.id, 64)}).`
|
|
2130
|
+
);
|
|
2131
|
+
} else {
|
|
2132
|
+
lines.push("No generated version is live for this cell.");
|
|
2133
|
+
}
|
|
2134
|
+
if ((_a = data.cell) == null ? void 0 : _a.rationale) lines.push(`Why this exists: ${untrusted(data.cell.rationale, 500)}`);
|
|
2135
|
+
if ((_b = data.cell) == null ? void 0 : _b.error) lines.push(`Last attempt failed: ${untrusted(data.cell.error, 300)}`);
|
|
2136
|
+
lines.push(data.verdict);
|
|
2137
|
+
if (data.performance) {
|
|
2138
|
+
lines.push(`Shown ${data.performance.shown} times, ${data.performance.conversions} conversions.`);
|
|
2139
|
+
}
|
|
2140
|
+
return {
|
|
2141
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
2142
|
+
structuredContent: data
|
|
2143
|
+
};
|
|
2144
|
+
})
|
|
2145
|
+
);
|
|
2146
|
+
server.registerTool(
|
|
2147
|
+
"generate_cell",
|
|
2148
|
+
{
|
|
2149
|
+
title: "Generate a cell version",
|
|
2150
|
+
description: "Queue AI generation of a version for one (persona, slot) cell. The worker writes it asynchronously (usually under a minute), brand-locked, and it goes live as a named option the optimizer explores. Also works to regenerate or retry a failed cell. Requires a paid plan (anonymous demo tokens are read-only).",
|
|
2151
|
+
inputSchema: {
|
|
2152
|
+
projectId: projectIdSchema,
|
|
2153
|
+
slotId: z15.string().min(1).max(128).describe("The slot id (a matrix column)"),
|
|
2154
|
+
persona: z15.string().min(1).max(64).describe("The persona key (a matrix row)")
|
|
2155
|
+
},
|
|
2156
|
+
outputSchema: {
|
|
2157
|
+
slotId: z15.string(),
|
|
2158
|
+
persona: z15.string(),
|
|
2159
|
+
status: z15.literal("pending").describe("Queued \u2014 check get_cell_matrix for progress")
|
|
2160
|
+
},
|
|
2161
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
2162
|
+
},
|
|
2163
|
+
withApiErrorGuidance(async ({ projectId, slotId, persona }) => {
|
|
2164
|
+
const id = encodeURIComponent(projectId);
|
|
2165
|
+
await client.post(`/projects/${id}/cells/generate`, { slotId, persona });
|
|
2166
|
+
return {
|
|
2167
|
+
content: [
|
|
2168
|
+
{
|
|
2169
|
+
type: "text",
|
|
2170
|
+
text: `Writing a version of ${untrusted(slotId, 64)} for ${untrusted(persona, 64)} \u2014 it usually goes live in under a minute. Check get_cell_matrix (status flips pending \u2192 live) or get_cell_detail for the story once it lands.`
|
|
2171
|
+
}
|
|
2172
|
+
],
|
|
2173
|
+
structuredContent: { slotId, persona, status: "pending" }
|
|
2174
|
+
};
|
|
2175
|
+
})
|
|
2176
|
+
);
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2021
2179
|
// src/server.ts
|
|
2022
|
-
var PKG_VERSION = true ? "0.
|
|
2180
|
+
var PKG_VERSION = true ? "0.18.0" : "0.0.0-dev";
|
|
2023
2181
|
function createMcpServer(client) {
|
|
2024
2182
|
const server = new McpServer(
|
|
2025
2183
|
{
|
|
@@ -2054,6 +2212,7 @@ function createMcpServer(client) {
|
|
|
2054
2212
|
registerVariantWriteTools(server, client);
|
|
2055
2213
|
registerIntegrationGuideTools(server);
|
|
2056
2214
|
registerAgentTrafficTools(server, client);
|
|
2215
|
+
registerCellTools(server, client);
|
|
2057
2216
|
return server;
|
|
2058
2217
|
}
|
|
2059
2218
|
|
package/dist/index.cjs
CHANGED
|
@@ -48,10 +48,9 @@ var ApiClient = class {
|
|
|
48
48
|
var _a2, _b;
|
|
49
49
|
const res = await fetch(`${this.baseUrl}/v1/mgmt${path}`, {
|
|
50
50
|
method,
|
|
51
|
-
headers: {
|
|
52
|
-
authorization: `Bearer ${this.apiKey}
|
|
53
|
-
|
|
54
|
-
},
|
|
51
|
+
headers: __spreadValues({
|
|
52
|
+
authorization: `Bearer ${this.apiKey}`
|
|
53
|
+
}, body !== void 0 ? { "content-type": "application/json" } : {}),
|
|
55
54
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
56
55
|
});
|
|
57
56
|
if (res.ok) return res.json();
|
|
@@ -1863,6 +1862,15 @@ useLayoutOrder(). Non-React sites: sections: ['#hero', '#pricing', '#faq'] (CSS
|
|
|
1863
1862
|
the theme's natural order) in window.sentient \u2014 the snippet reorders those elements within
|
|
1864
1863
|
their shared parent, and applies nothing if any selector drifts.
|
|
1865
1864
|
|
|
1865
|
+
## No-code install order (snippet)
|
|
1866
|
+
|
|
1867
|
+
Three tags in <head>, in this order: the window.sentient config, the inline pre-paint script,
|
|
1868
|
+
then the deferred loader. The middle tag is optional but recommended \u2014 the loader is deferred,
|
|
1869
|
+
so on a return visit the page can paint its original state before the loader runs; the inline
|
|
1870
|
+
script applies the last served decision from the visitor's own device first, with no network
|
|
1871
|
+
call and no cloak. Get its exact bytes from the dashboard's Install page, or from
|
|
1872
|
+
renderSnippetPrePaintScript() in '@sentientui/snippet/install'.
|
|
1873
|
+
|
|
1866
1874
|
## Testing the integration
|
|
1867
1875
|
|
|
1868
1876
|
Use '@sentientui/react/testing': renderWithSentient(ui, { variants, slots, persona }) forces
|
|
@@ -2024,8 +2032,158 @@ function registerAgentTrafficTools(server, client) {
|
|
|
2024
2032
|
);
|
|
2025
2033
|
}
|
|
2026
2034
|
|
|
2035
|
+
// src/tools/cells.ts
|
|
2036
|
+
var import_zod15 = require("zod");
|
|
2037
|
+
var personaOutput = import_zod15.z.object({
|
|
2038
|
+
key: import_zod15.z.string(),
|
|
2039
|
+
displayName: import_zod15.z.string(),
|
|
2040
|
+
description: import_zod15.z.string().nullable(),
|
|
2041
|
+
share: import_zod15.z.number().describe("Fraction of 30-day decided sessions")
|
|
2042
|
+
});
|
|
2043
|
+
var cellOutput = import_zod15.z.object({
|
|
2044
|
+
slotId: import_zod15.z.string(),
|
|
2045
|
+
persona: import_zod15.z.string(),
|
|
2046
|
+
status: import_zod15.z.string().describe("pending | generating | live | error | rejected"),
|
|
2047
|
+
requestedBy: import_zod15.z.string(),
|
|
2048
|
+
armId: import_zod15.z.string().nullable(),
|
|
2049
|
+
rationale: import_zod15.z.string().nullable(),
|
|
2050
|
+
error: import_zod15.z.string().nullable(),
|
|
2051
|
+
updatedAt: import_zod15.z.string(),
|
|
2052
|
+
performance: import_zod15.z.object({
|
|
2053
|
+
shown: import_zod15.z.number(),
|
|
2054
|
+
conversions: import_zod15.z.number(),
|
|
2055
|
+
deltaPct: import_zod15.z.number().nullable().describe("Signed % vs the original conversion rate"),
|
|
2056
|
+
tier: import_zod15.z.string().describe("early | no_baseline | winning | losing | flat")
|
|
2057
|
+
}).optional().describe("Present on live cells: the at-a-glance readout")
|
|
2058
|
+
});
|
|
2059
|
+
function registerCellTools(server, client) {
|
|
2060
|
+
server.registerTool(
|
|
2061
|
+
"get_cell_matrix",
|
|
2062
|
+
{
|
|
2063
|
+
title: "Persona \xD7 slot cell matrix",
|
|
2064
|
+
description: 'The "Who sees what" matrix: which visitor types (personas) have an AI-generated version live in each personalizable region (slot), with per-persona traffic share and the first-miss auto-fill setting. A cell with no row means that visitor type sees the original.' + UNTRUSTED_FIELDS_NOTE,
|
|
2065
|
+
inputSchema: { projectId: projectIdSchema },
|
|
2066
|
+
outputSchema: {
|
|
2067
|
+
slots: import_zod15.z.array(import_zod15.z.object({ slotId: import_zod15.z.string(), displayName: import_zod15.z.string(), supportsForms: import_zod15.z.boolean() })),
|
|
2068
|
+
personas: import_zod15.z.array(personaOutput),
|
|
2069
|
+
cells: import_zod15.z.array(cellOutput),
|
|
2070
|
+
autoFill: import_zod15.z.boolean(),
|
|
2071
|
+
personaSource: import_zod15.z.string().describe("Active persona-set source: default | declared | discovered"),
|
|
2072
|
+
undecidedShare: import_zod15.z.number()
|
|
2073
|
+
},
|
|
2074
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
|
|
2075
|
+
},
|
|
2076
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
2077
|
+
const id = encodeURIComponent(projectId);
|
|
2078
|
+
const data = await client.get(`/projects/${id}/cells`);
|
|
2079
|
+
const byCell = new Map(data.cells.map((c) => [`${c.slotId}::${c.persona}`, c]));
|
|
2080
|
+
const lines = [
|
|
2081
|
+
`Slots: ${data.slots.map((s) => untrusted(s.displayName)).join(", ") || "(none published)"}`,
|
|
2082
|
+
`Auto-fill on first miss: ${data.autoFill ? "ON" : "off"} \xB7 persona source: ${data.personaSource}`
|
|
2083
|
+
];
|
|
2084
|
+
for (const p of data.personas) {
|
|
2085
|
+
const states = data.slots.map((s) => {
|
|
2086
|
+
const c = byCell.get(`${s.slotId}::${p.key}`);
|
|
2087
|
+
return `${untrusted(s.displayName, 40)}: ${c ? c.status : "original"}`;
|
|
2088
|
+
});
|
|
2089
|
+
lines.push(`- ${untrusted(p.displayName)} (${Math.round(p.share * 100)}% of visitors) \u2014 ${states.join(" \xB7 ")}`);
|
|
2090
|
+
}
|
|
2091
|
+
lines.push(`Undecided visitors (${Math.round(data.undecidedShare * 100)}%) always see the original.`);
|
|
2092
|
+
return {
|
|
2093
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
2094
|
+
structuredContent: data
|
|
2095
|
+
};
|
|
2096
|
+
})
|
|
2097
|
+
);
|
|
2098
|
+
server.registerTool(
|
|
2099
|
+
"get_cell_detail",
|
|
2100
|
+
{
|
|
2101
|
+
title: "Cell story",
|
|
2102
|
+
description: "One cell's full story: the generated option a visitor type sees in a slot, why it was written (the stored generation rationale), and how it is performing vs the original (deterministic plain-language verdict)." + UNTRUSTED_FIELDS_NOTE,
|
|
2103
|
+
inputSchema: {
|
|
2104
|
+
projectId: projectIdSchema,
|
|
2105
|
+
slotId: import_zod15.z.string().min(1).max(128).describe("The slot id (a matrix column)"),
|
|
2106
|
+
persona: import_zod15.z.string().min(1).max(64).describe("The persona key (a matrix row)")
|
|
2107
|
+
},
|
|
2108
|
+
outputSchema: {
|
|
2109
|
+
cell: cellOutput.nullable(),
|
|
2110
|
+
arm: import_zod15.z.object({
|
|
2111
|
+
id: import_zod15.z.string(),
|
|
2112
|
+
displayName: import_zod15.z.string().nullable(),
|
|
2113
|
+
content: import_zod15.z.string().nullable(),
|
|
2114
|
+
blocks: import_zod15.z.unknown().nullable()
|
|
2115
|
+
}).nullable(),
|
|
2116
|
+
performance: import_zod15.z.object({
|
|
2117
|
+
shown: import_zod15.z.number(),
|
|
2118
|
+
conversions: import_zod15.z.number(),
|
|
2119
|
+
conversionRate: import_zod15.z.number(),
|
|
2120
|
+
baselineShown: import_zod15.z.number(),
|
|
2121
|
+
baselineConversions: import_zod15.z.number(),
|
|
2122
|
+
baselineConversionRate: import_zod15.z.number()
|
|
2123
|
+
}).nullable(),
|
|
2124
|
+
verdict: import_zod15.z.string()
|
|
2125
|
+
},
|
|
2126
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
|
|
2127
|
+
},
|
|
2128
|
+
withApiErrorGuidance(async ({ projectId, slotId, persona }) => {
|
|
2129
|
+
var _a2, _b;
|
|
2130
|
+
const id = encodeURIComponent(projectId);
|
|
2131
|
+
const data = await client.get(`/projects/${id}/cells/detail?slotId=${encodeURIComponent(slotId)}&persona=${encodeURIComponent(persona)}`);
|
|
2132
|
+
const lines = [];
|
|
2133
|
+
if (data.arm) {
|
|
2134
|
+
lines.push(
|
|
2135
|
+
data.arm.content !== null ? `They see: "${untrusted(data.arm.content, 300)}"` : `They see a designed layout (option ${untrusted(data.arm.id, 64)}).`
|
|
2136
|
+
);
|
|
2137
|
+
} else {
|
|
2138
|
+
lines.push("No generated version is live for this cell.");
|
|
2139
|
+
}
|
|
2140
|
+
if ((_a2 = data.cell) == null ? void 0 : _a2.rationale) lines.push(`Why this exists: ${untrusted(data.cell.rationale, 500)}`);
|
|
2141
|
+
if ((_b = data.cell) == null ? void 0 : _b.error) lines.push(`Last attempt failed: ${untrusted(data.cell.error, 300)}`);
|
|
2142
|
+
lines.push(data.verdict);
|
|
2143
|
+
if (data.performance) {
|
|
2144
|
+
lines.push(`Shown ${data.performance.shown} times, ${data.performance.conversions} conversions.`);
|
|
2145
|
+
}
|
|
2146
|
+
return {
|
|
2147
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
2148
|
+
structuredContent: data
|
|
2149
|
+
};
|
|
2150
|
+
})
|
|
2151
|
+
);
|
|
2152
|
+
server.registerTool(
|
|
2153
|
+
"generate_cell",
|
|
2154
|
+
{
|
|
2155
|
+
title: "Generate a cell version",
|
|
2156
|
+
description: "Queue AI generation of a version for one (persona, slot) cell. The worker writes it asynchronously (usually under a minute), brand-locked, and it goes live as a named option the optimizer explores. Also works to regenerate or retry a failed cell. Requires a paid plan (anonymous demo tokens are read-only).",
|
|
2157
|
+
inputSchema: {
|
|
2158
|
+
projectId: projectIdSchema,
|
|
2159
|
+
slotId: import_zod15.z.string().min(1).max(128).describe("The slot id (a matrix column)"),
|
|
2160
|
+
persona: import_zod15.z.string().min(1).max(64).describe("The persona key (a matrix row)")
|
|
2161
|
+
},
|
|
2162
|
+
outputSchema: {
|
|
2163
|
+
slotId: import_zod15.z.string(),
|
|
2164
|
+
persona: import_zod15.z.string(),
|
|
2165
|
+
status: import_zod15.z.literal("pending").describe("Queued \u2014 check get_cell_matrix for progress")
|
|
2166
|
+
},
|
|
2167
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
2168
|
+
},
|
|
2169
|
+
withApiErrorGuidance(async ({ projectId, slotId, persona }) => {
|
|
2170
|
+
const id = encodeURIComponent(projectId);
|
|
2171
|
+
await client.post(`/projects/${id}/cells/generate`, { slotId, persona });
|
|
2172
|
+
return {
|
|
2173
|
+
content: [
|
|
2174
|
+
{
|
|
2175
|
+
type: "text",
|
|
2176
|
+
text: `Writing a version of ${untrusted(slotId, 64)} for ${untrusted(persona, 64)} \u2014 it usually goes live in under a minute. Check get_cell_matrix (status flips pending \u2192 live) or get_cell_detail for the story once it lands.`
|
|
2177
|
+
}
|
|
2178
|
+
],
|
|
2179
|
+
structuredContent: { slotId, persona, status: "pending" }
|
|
2180
|
+
};
|
|
2181
|
+
})
|
|
2182
|
+
);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2027
2185
|
// src/server.ts
|
|
2028
|
-
var PKG_VERSION = true ? "0.
|
|
2186
|
+
var PKG_VERSION = true ? "0.18.0" : "0.0.0-dev";
|
|
2029
2187
|
function createMcpServer(client) {
|
|
2030
2188
|
const server = new import_mcp.McpServer(
|
|
2031
2189
|
{
|
|
@@ -2060,6 +2218,7 @@ function createMcpServer(client) {
|
|
|
2060
2218
|
registerVariantWriteTools(server, client);
|
|
2061
2219
|
registerIntegrationGuideTools(server);
|
|
2062
2220
|
registerAgentTrafficTools(server, client);
|
|
2221
|
+
registerCellTools(server, client);
|
|
2063
2222
|
return server;
|
|
2064
2223
|
}
|
|
2065
2224
|
|
package/dist/index.js
CHANGED
package/dist/lib.cjs
CHANGED
|
@@ -79,10 +79,9 @@ var ApiClient = class {
|
|
|
79
79
|
var _a, _b;
|
|
80
80
|
const res = await fetch(`${this.baseUrl}/v1/mgmt${path}`, {
|
|
81
81
|
method,
|
|
82
|
-
headers: {
|
|
83
|
-
authorization: `Bearer ${this.apiKey}
|
|
84
|
-
|
|
85
|
-
},
|
|
82
|
+
headers: __spreadValues({
|
|
83
|
+
authorization: `Bearer ${this.apiKey}`
|
|
84
|
+
}, body !== void 0 ? { "content-type": "application/json" } : {}),
|
|
86
85
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
87
86
|
});
|
|
88
87
|
if (res.ok) return res.json();
|
|
@@ -1887,6 +1886,15 @@ useLayoutOrder(). Non-React sites: sections: ['#hero', '#pricing', '#faq'] (CSS
|
|
|
1887
1886
|
the theme's natural order) in window.sentient \u2014 the snippet reorders those elements within
|
|
1888
1887
|
their shared parent, and applies nothing if any selector drifts.
|
|
1889
1888
|
|
|
1889
|
+
## No-code install order (snippet)
|
|
1890
|
+
|
|
1891
|
+
Three tags in <head>, in this order: the window.sentient config, the inline pre-paint script,
|
|
1892
|
+
then the deferred loader. The middle tag is optional but recommended \u2014 the loader is deferred,
|
|
1893
|
+
so on a return visit the page can paint its original state before the loader runs; the inline
|
|
1894
|
+
script applies the last served decision from the visitor's own device first, with no network
|
|
1895
|
+
call and no cloak. Get its exact bytes from the dashboard's Install page, or from
|
|
1896
|
+
renderSnippetPrePaintScript() in '@sentientui/snippet/install'.
|
|
1897
|
+
|
|
1890
1898
|
## Testing the integration
|
|
1891
1899
|
|
|
1892
1900
|
Use '@sentientui/react/testing': renderWithSentient(ui, { variants, slots, persona }) forces
|
|
@@ -2048,8 +2056,158 @@ function registerAgentTrafficTools(server, client) {
|
|
|
2048
2056
|
);
|
|
2049
2057
|
}
|
|
2050
2058
|
|
|
2059
|
+
// src/tools/cells.ts
|
|
2060
|
+
var import_zod15 = require("zod");
|
|
2061
|
+
var personaOutput = import_zod15.z.object({
|
|
2062
|
+
key: import_zod15.z.string(),
|
|
2063
|
+
displayName: import_zod15.z.string(),
|
|
2064
|
+
description: import_zod15.z.string().nullable(),
|
|
2065
|
+
share: import_zod15.z.number().describe("Fraction of 30-day decided sessions")
|
|
2066
|
+
});
|
|
2067
|
+
var cellOutput = import_zod15.z.object({
|
|
2068
|
+
slotId: import_zod15.z.string(),
|
|
2069
|
+
persona: import_zod15.z.string(),
|
|
2070
|
+
status: import_zod15.z.string().describe("pending | generating | live | error | rejected"),
|
|
2071
|
+
requestedBy: import_zod15.z.string(),
|
|
2072
|
+
armId: import_zod15.z.string().nullable(),
|
|
2073
|
+
rationale: import_zod15.z.string().nullable(),
|
|
2074
|
+
error: import_zod15.z.string().nullable(),
|
|
2075
|
+
updatedAt: import_zod15.z.string(),
|
|
2076
|
+
performance: import_zod15.z.object({
|
|
2077
|
+
shown: import_zod15.z.number(),
|
|
2078
|
+
conversions: import_zod15.z.number(),
|
|
2079
|
+
deltaPct: import_zod15.z.number().nullable().describe("Signed % vs the original conversion rate"),
|
|
2080
|
+
tier: import_zod15.z.string().describe("early | no_baseline | winning | losing | flat")
|
|
2081
|
+
}).optional().describe("Present on live cells: the at-a-glance readout")
|
|
2082
|
+
});
|
|
2083
|
+
function registerCellTools(server, client) {
|
|
2084
|
+
server.registerTool(
|
|
2085
|
+
"get_cell_matrix",
|
|
2086
|
+
{
|
|
2087
|
+
title: "Persona \xD7 slot cell matrix",
|
|
2088
|
+
description: 'The "Who sees what" matrix: which visitor types (personas) have an AI-generated version live in each personalizable region (slot), with per-persona traffic share and the first-miss auto-fill setting. A cell with no row means that visitor type sees the original.' + UNTRUSTED_FIELDS_NOTE,
|
|
2089
|
+
inputSchema: { projectId: projectIdSchema },
|
|
2090
|
+
outputSchema: {
|
|
2091
|
+
slots: import_zod15.z.array(import_zod15.z.object({ slotId: import_zod15.z.string(), displayName: import_zod15.z.string(), supportsForms: import_zod15.z.boolean() })),
|
|
2092
|
+
personas: import_zod15.z.array(personaOutput),
|
|
2093
|
+
cells: import_zod15.z.array(cellOutput),
|
|
2094
|
+
autoFill: import_zod15.z.boolean(),
|
|
2095
|
+
personaSource: import_zod15.z.string().describe("Active persona-set source: default | declared | discovered"),
|
|
2096
|
+
undecidedShare: import_zod15.z.number()
|
|
2097
|
+
},
|
|
2098
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
|
|
2099
|
+
},
|
|
2100
|
+
withApiErrorGuidance(async ({ projectId }) => {
|
|
2101
|
+
const id = encodeURIComponent(projectId);
|
|
2102
|
+
const data = await client.get(`/projects/${id}/cells`);
|
|
2103
|
+
const byCell = new Map(data.cells.map((c) => [`${c.slotId}::${c.persona}`, c]));
|
|
2104
|
+
const lines = [
|
|
2105
|
+
`Slots: ${data.slots.map((s) => untrusted(s.displayName)).join(", ") || "(none published)"}`,
|
|
2106
|
+
`Auto-fill on first miss: ${data.autoFill ? "ON" : "off"} \xB7 persona source: ${data.personaSource}`
|
|
2107
|
+
];
|
|
2108
|
+
for (const p of data.personas) {
|
|
2109
|
+
const states = data.slots.map((s) => {
|
|
2110
|
+
const c = byCell.get(`${s.slotId}::${p.key}`);
|
|
2111
|
+
return `${untrusted(s.displayName, 40)}: ${c ? c.status : "original"}`;
|
|
2112
|
+
});
|
|
2113
|
+
lines.push(`- ${untrusted(p.displayName)} (${Math.round(p.share * 100)}% of visitors) \u2014 ${states.join(" \xB7 ")}`);
|
|
2114
|
+
}
|
|
2115
|
+
lines.push(`Undecided visitors (${Math.round(data.undecidedShare * 100)}%) always see the original.`);
|
|
2116
|
+
return {
|
|
2117
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
2118
|
+
structuredContent: data
|
|
2119
|
+
};
|
|
2120
|
+
})
|
|
2121
|
+
);
|
|
2122
|
+
server.registerTool(
|
|
2123
|
+
"get_cell_detail",
|
|
2124
|
+
{
|
|
2125
|
+
title: "Cell story",
|
|
2126
|
+
description: "One cell's full story: the generated option a visitor type sees in a slot, why it was written (the stored generation rationale), and how it is performing vs the original (deterministic plain-language verdict)." + UNTRUSTED_FIELDS_NOTE,
|
|
2127
|
+
inputSchema: {
|
|
2128
|
+
projectId: projectIdSchema,
|
|
2129
|
+
slotId: import_zod15.z.string().min(1).max(128).describe("The slot id (a matrix column)"),
|
|
2130
|
+
persona: import_zod15.z.string().min(1).max(64).describe("The persona key (a matrix row)")
|
|
2131
|
+
},
|
|
2132
|
+
outputSchema: {
|
|
2133
|
+
cell: cellOutput.nullable(),
|
|
2134
|
+
arm: import_zod15.z.object({
|
|
2135
|
+
id: import_zod15.z.string(),
|
|
2136
|
+
displayName: import_zod15.z.string().nullable(),
|
|
2137
|
+
content: import_zod15.z.string().nullable(),
|
|
2138
|
+
blocks: import_zod15.z.unknown().nullable()
|
|
2139
|
+
}).nullable(),
|
|
2140
|
+
performance: import_zod15.z.object({
|
|
2141
|
+
shown: import_zod15.z.number(),
|
|
2142
|
+
conversions: import_zod15.z.number(),
|
|
2143
|
+
conversionRate: import_zod15.z.number(),
|
|
2144
|
+
baselineShown: import_zod15.z.number(),
|
|
2145
|
+
baselineConversions: import_zod15.z.number(),
|
|
2146
|
+
baselineConversionRate: import_zod15.z.number()
|
|
2147
|
+
}).nullable(),
|
|
2148
|
+
verdict: import_zod15.z.string()
|
|
2149
|
+
},
|
|
2150
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
|
|
2151
|
+
},
|
|
2152
|
+
withApiErrorGuidance(async ({ projectId, slotId, persona }) => {
|
|
2153
|
+
var _a, _b;
|
|
2154
|
+
const id = encodeURIComponent(projectId);
|
|
2155
|
+
const data = await client.get(`/projects/${id}/cells/detail?slotId=${encodeURIComponent(slotId)}&persona=${encodeURIComponent(persona)}`);
|
|
2156
|
+
const lines = [];
|
|
2157
|
+
if (data.arm) {
|
|
2158
|
+
lines.push(
|
|
2159
|
+
data.arm.content !== null ? `They see: "${untrusted(data.arm.content, 300)}"` : `They see a designed layout (option ${untrusted(data.arm.id, 64)}).`
|
|
2160
|
+
);
|
|
2161
|
+
} else {
|
|
2162
|
+
lines.push("No generated version is live for this cell.");
|
|
2163
|
+
}
|
|
2164
|
+
if ((_a = data.cell) == null ? void 0 : _a.rationale) lines.push(`Why this exists: ${untrusted(data.cell.rationale, 500)}`);
|
|
2165
|
+
if ((_b = data.cell) == null ? void 0 : _b.error) lines.push(`Last attempt failed: ${untrusted(data.cell.error, 300)}`);
|
|
2166
|
+
lines.push(data.verdict);
|
|
2167
|
+
if (data.performance) {
|
|
2168
|
+
lines.push(`Shown ${data.performance.shown} times, ${data.performance.conversions} conversions.`);
|
|
2169
|
+
}
|
|
2170
|
+
return {
|
|
2171
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
2172
|
+
structuredContent: data
|
|
2173
|
+
};
|
|
2174
|
+
})
|
|
2175
|
+
);
|
|
2176
|
+
server.registerTool(
|
|
2177
|
+
"generate_cell",
|
|
2178
|
+
{
|
|
2179
|
+
title: "Generate a cell version",
|
|
2180
|
+
description: "Queue AI generation of a version for one (persona, slot) cell. The worker writes it asynchronously (usually under a minute), brand-locked, and it goes live as a named option the optimizer explores. Also works to regenerate or retry a failed cell. Requires a paid plan (anonymous demo tokens are read-only).",
|
|
2181
|
+
inputSchema: {
|
|
2182
|
+
projectId: projectIdSchema,
|
|
2183
|
+
slotId: import_zod15.z.string().min(1).max(128).describe("The slot id (a matrix column)"),
|
|
2184
|
+
persona: import_zod15.z.string().min(1).max(64).describe("The persona key (a matrix row)")
|
|
2185
|
+
},
|
|
2186
|
+
outputSchema: {
|
|
2187
|
+
slotId: import_zod15.z.string(),
|
|
2188
|
+
persona: import_zod15.z.string(),
|
|
2189
|
+
status: import_zod15.z.literal("pending").describe("Queued \u2014 check get_cell_matrix for progress")
|
|
2190
|
+
},
|
|
2191
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
2192
|
+
},
|
|
2193
|
+
withApiErrorGuidance(async ({ projectId, slotId, persona }) => {
|
|
2194
|
+
const id = encodeURIComponent(projectId);
|
|
2195
|
+
await client.post(`/projects/${id}/cells/generate`, { slotId, persona });
|
|
2196
|
+
return {
|
|
2197
|
+
content: [
|
|
2198
|
+
{
|
|
2199
|
+
type: "text",
|
|
2200
|
+
text: `Writing a version of ${untrusted(slotId, 64)} for ${untrusted(persona, 64)} \u2014 it usually goes live in under a minute. Check get_cell_matrix (status flips pending \u2192 live) or get_cell_detail for the story once it lands.`
|
|
2201
|
+
}
|
|
2202
|
+
],
|
|
2203
|
+
structuredContent: { slotId, persona, status: "pending" }
|
|
2204
|
+
};
|
|
2205
|
+
})
|
|
2206
|
+
);
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2051
2209
|
// src/server.ts
|
|
2052
|
-
var PKG_VERSION = true ? "0.
|
|
2210
|
+
var PKG_VERSION = true ? "0.18.0" : "0.0.0-dev";
|
|
2053
2211
|
function createMcpServer(client) {
|
|
2054
2212
|
const server = new import_mcp.McpServer(
|
|
2055
2213
|
{
|
|
@@ -2084,6 +2242,7 @@ function createMcpServer(client) {
|
|
|
2084
2242
|
registerVariantWriteTools(server, client);
|
|
2085
2243
|
registerIntegrationGuideTools(server);
|
|
2086
2244
|
registerAgentTrafficTools(server, client);
|
|
2245
|
+
registerCellTools(server, client);
|
|
2087
2246
|
return server;
|
|
2088
2247
|
}
|
|
2089
2248
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/lib.js
CHANGED