@sentientui/mcp 0.16.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -33,7 +33,7 @@ Server keys start with `sk_` — required for the management API. Public keys (`
33
33
  | `create_project` | Onboard a new user: creates a project + returns its `pk_` key. Needs an account login (OAuth) — refuses `sk_` project keys and demo tokens. Follow with `get_integration_guide` |
34
34
  | `get_project_stats` | Health status + 24h event/session volume |
35
35
  | `list_components` | Components + variant counts + impression totals |
36
- | `get_variant_performance` | CVR 7d vs prior 7d + momentum direction |
36
+ | `get_variant_performance` | CVR current vs prior window + momentum (range/from/to params; default 7d) |
37
37
  | `get_persona_breakdown` | Cluster distribution + avg reliability per cluster |
38
38
  | `get_goal_funnel` | Goal hit counts + per-variant completion rates |
39
39
  | `list_guardrail_events` | Auto-paused variants (last 24h) |
package/README.md CHANGED
@@ -88,11 +88,11 @@ A sandboxed demo token is provisioned automatically — 10 calls/month, read-onl
88
88
  | `create_project` | Create a new project and return its `pk_` public key (account login required — not usable with an `sk_` project key or demo token) |
89
89
  | `get_project_stats` | Events, sessions, agent calls, and health status |
90
90
  | `list_components` | All adaptive components with variant counts |
91
- | `get_variant_performance` | CVR, momentum, and impressions per variant (7d vs prior 7d) |
91
+ | `get_variant_performance` | CVR, momentum, and impressions per variant (windowed: range 7d/30d/90d/all or from/to; default 7d vs prior 7d) |
92
92
  | `get_insights` | AI-generated narrator observations and advisor recommendations |
93
93
  | `refresh_insights` | Trigger fresh AI insight generation |
94
94
  | `get_persona_breakdown` | Visitor cluster distribution with reliability scores |
95
- | `get_goal_funnel` | Goal hit counts and conversion rates per variant |
95
+ | `get_goal_funnel` | Goal hit counts and conversion rates per variant (windowed; default 30d) |
96
96
  | `list_goals` | Defined goals (id, role, event, status) — includes goals with no conversions yet, so agents can wire a dashboard-defined goal into code |
97
97
  | `list_guardrail_events` | Variants auto-paused by the guardrail (last 24h) |
98
98
  | `get_layout_stats` | Per-persona section layout rankings and reward weights |
@@ -199,7 +199,11 @@ var RENDER_JS = {
199
199
  "variant-performance": `
200
200
  window.__render = function(data){
201
201
  var variants = (data && data.variants) || [];
202
- el('sub').textContent = variants.length + ' variant' + (variants.length === 1 ? '' : 's') + ' \xB7 conversion, last 7 days vs prior 7';
202
+ var winText = 'current vs prior period';
203
+ if (data && data.window && data.window.start && data.window.end) {
204
+ winText = String(data.window.start).slice(0, 10) + ' to ' + String(data.window.end).slice(0, 10) + ' vs prior period';
205
+ }
206
+ el('sub').textContent = variants.length + ' variant' + (variants.length === 1 ? '' : 's') + ' \xB7 conversion, ' + winText;
203
207
  if (!variants.length) { showEmpty('No variant data available yet.'); return; }
204
208
  var max = variants.reduce(function(m,v){ return Math.max(m, v.currentCvr || 0); }, 0) || 1;
205
209
  var sorted = variants.slice().sort(function(a,b){ return (b.currentCvr||0) - (a.currentCvr||0); });
@@ -331,6 +335,44 @@ import { z as z2 } from "zod";
331
335
  // src/tools/common.ts
332
336
  import { z } from "zod";
333
337
  var projectIdSchema = z.string().uuid().describe("The project UUID");
338
+ function rangeInputSchema(defaultRange) {
339
+ return {
340
+ range: z.enum(["7d", "30d", "90d", "all"]).optional().describe(
341
+ `Calendar window in the project's reporting timezone (default ${defaultRange}). 'all' means all retained data \u2014 the plan's retention window bounds every lookback.`
342
+ ),
343
+ from: z.string().optional().describe(
344
+ "Custom window start (YYYY-MM-DD; overrides range). Must be within the plan retention window."
345
+ ),
346
+ to: z.string().optional().describe("Custom window end (YYYY-MM-DD, inclusive; default today).")
347
+ };
348
+ }
349
+ function rangeQuery(args) {
350
+ const p = new URLSearchParams();
351
+ if (args.from) {
352
+ p.set("from", args.from);
353
+ if (args.to) p.set("to", args.to);
354
+ } else if (args.range) {
355
+ p.set("range", args.range);
356
+ }
357
+ const s = p.toString();
358
+ return s ? `?${s}` : "";
359
+ }
360
+ var windowOutputSchema = z.object({
361
+ range: z.string(),
362
+ start: z.string().nullable(),
363
+ end: z.string().nullable(),
364
+ timezone: z.string(),
365
+ bucketUnit: z.string(),
366
+ retentionDays: z.number().describe("The plan's effective data retention in days"),
367
+ earliestAvailable: z.string().describe("First date retention still holds data for (YYYY-MM-DD)")
368
+ }).optional().describe("The window the server actually used for every count in this response");
369
+ function windowLine(window) {
370
+ if (!window) return "";
371
+ if (window.start && window.end) {
372
+ return `Window: ${window.start.slice(0, 10)} to ${window.end.slice(0, 10)} (${window.timezone}, exclusive end)`;
373
+ }
374
+ return `Window: all retained data (${window.retentionDays}-day retention)`;
375
+ }
334
376
  function apiErrorGuidance(err, extra) {
335
377
  if (extra && Object.prototype.hasOwnProperty.call(extra, err.message)) {
336
378
  return extra[err.message];
@@ -345,6 +387,12 @@ function apiErrorGuidance(err, extra) {
345
387
  default:
346
388
  break;
347
389
  }
390
+ if (err.message === "range_out_of_retention") {
391
+ return "The requested window starts before this plan retains data \u2014 older events have been deleted by the retention purge and cannot be recovered. Ask for a more recent window, or upgrade the SentientUI plan for longer history (Growth keeps 180 days, Scale 365).";
392
+ }
393
+ if (err.message === "invalid_range") {
394
+ return "Invalid time window. Use range '7d', '30d', '90d' or 'all', or from/to as YYYY-MM-DD dates.";
395
+ }
348
396
  if (err.status === 402) {
349
397
  return `This feature requires a higher plan (${err.message}). Upgrade your SentientUI plan, then try again.`;
350
398
  }
@@ -516,8 +564,8 @@ function registerComponentTools(server, client) {
516
564
  "list_components",
517
565
  {
518
566
  title: "List components",
519
- description: "List all adaptive components in a project with variant counts and impression totals.",
520
- inputSchema: { projectId: projectIdSchema },
567
+ description: "List all adaptive components in a project with variant counts and impression totals. Counts cover all retained data by default; pass range or from/to for a window.",
568
+ inputSchema: __spreadValues({ projectId: projectIdSchema }, rangeInputSchema("all")),
521
569
  outputSchema: {
522
570
  components: z3.array(
523
571
  z3.object({
@@ -526,7 +574,8 @@ function registerComponentTools(server, client) {
526
574
  impressions: z3.number(),
527
575
  conversions: z3.number()
528
576
  })
529
- ).describe("Adaptive components in the project (empty if none)")
577
+ ).describe("Adaptive components in the project (empty if none)"),
578
+ window: windowOutputSchema
530
579
  },
531
580
  annotations: {
532
581
  readOnlyHint: true,
@@ -534,16 +583,17 @@ function registerComponentTools(server, client) {
534
583
  openWorldHint: false
535
584
  }
536
585
  },
537
- withApiErrorGuidance(async ({ projectId }) => {
586
+ withApiErrorGuidance(async ({ projectId, range, from, to }) => {
538
587
  const id = encodeURIComponent(projectId);
539
- const { components } = await client.get(`/projects/${id}/components`);
588
+ const { components, window } = await client.get(`/projects/${id}/components${rangeQuery({ range, from, to })}`);
540
589
  const structuredContent = {
541
590
  components: components.map((c) => ({
542
591
  componentId: c.component_id,
543
592
  variantCount: c.variants.length,
544
593
  impressions: c.total_impressions,
545
594
  conversions: c.total_conversions
546
- }))
595
+ })),
596
+ window
547
597
  };
548
598
  if (!components.length) {
549
599
  return {
@@ -561,19 +611,20 @@ function registerComponentTools(server, client) {
561
611
  "get_variant_performance",
562
612
  {
563
613
  title: "Variant performance",
564
- description: "Get CVR and momentum for all variants in a project over the last 7 days vs prior 7 days.",
565
- inputSchema: { projectId: projectIdSchema },
614
+ description: "Get CVR and momentum for all variants in a project over the selected window vs the immediately-preceding window of equal length (default: last 7 calendar days vs prior 7).",
615
+ inputSchema: __spreadValues({ projectId: projectIdSchema }, rangeInputSchema("7d")),
566
616
  _meta: uiMeta("variant-performance"),
567
617
  outputSchema: {
568
618
  variants: z3.array(
569
619
  z3.object({
570
620
  variantId: z3.string(),
571
- currentCvr: z3.number().describe("Conversion rate over the last 7 days (0-1)"),
572
- priorCvr: z3.number().describe("Conversion rate over the prior 7 days (0-1)"),
621
+ currentCvr: z3.number().describe("Conversion rate over the selected window (0-1)"),
622
+ priorCvr: z3.number().describe("Conversion rate over the preceding window (0-1)"),
573
623
  deltaPp: z3.number().describe("Change in percentage points"),
574
624
  momentum: z3.string().describe("Momentum direction: gaining, losing, or stable")
575
625
  })
576
- ).describe("Per-variant performance (empty if no data yet)")
626
+ ).describe("Per-variant performance (empty if no data yet)"),
627
+ window: windowOutputSchema
577
628
  },
578
629
  annotations: {
579
630
  readOnlyHint: true,
@@ -581,10 +632,10 @@ function registerComponentTools(server, client) {
581
632
  openWorldHint: false
582
633
  }
583
634
  },
584
- withApiErrorGuidance(async ({ projectId }) => {
635
+ withApiErrorGuidance(async ({ projectId, range, from, to }) => {
585
636
  var _a, _b, _c;
586
637
  const id = encodeURIComponent(projectId);
587
- const data = await client.get(`/projects/${id}/trends`);
638
+ const data = await client.get(`/projects/${id}/trends${rangeQuery({ range, from, to })}`);
588
639
  const momentumMap = new Map(((_a = data.momentum) != null ? _a : []).map((m) => [m.variantId, m.direction]));
589
640
  const structuredContent = {
590
641
  variants: ((_b = data.cvr) != null ? _b : []).map((v) => {
@@ -596,7 +647,8 @@ function registerComponentTools(server, client) {
596
647
  deltaPp: v.deltaPp,
597
648
  momentum: (_a2 = momentumMap.get(v.variantId)) != null ? _a2 : "stable"
598
649
  };
599
- })
650
+ }),
651
+ window: data.window
600
652
  };
601
653
  if (!((_c = data.cvr) == null ? void 0 : _c.length)) {
602
654
  return {
@@ -605,12 +657,14 @@ function registerComponentTools(server, client) {
605
657
  _meta: uiMeta("variant-performance")
606
658
  };
607
659
  }
608
- const text = data.cvr.map(
660
+ const lines = data.cvr.map(
609
661
  (v) => {
610
662
  var _a2;
611
663
  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"})`;
612
664
  }
613
- ).join("\n");
665
+ );
666
+ const win = windowLine(data.window);
667
+ const text = (win ? [win, ...lines] : lines).join("\n");
614
668
  return { content: [{ type: "text", text }], structuredContent, _meta: uiMeta("variant-performance") };
615
669
  })
616
670
  );
@@ -796,8 +850,8 @@ function registerGoalTools(server, client) {
796
850
  "get_goal_funnel",
797
851
  {
798
852
  title: "Goal funnel",
799
- description: "Get goal hit counts, unique-session conversion rates, and per-variant breakdown. This is a flat per-goal list \u2014 for multi-step funnel drop-off, use get_funnel_report.",
800
- inputSchema: { projectId: projectIdSchema },
853
+ description: "Get goal hit counts, unique-session conversion rates, and per-variant breakdown (default window: last 30 calendar days). This is a flat per-goal list \u2014 for multi-step funnel drop-off, use get_funnel_report.",
854
+ inputSchema: __spreadValues({ projectId: projectIdSchema }, rangeInputSchema("30d")),
801
855
  _meta: uiMeta("goal-funnel"),
802
856
  outputSchema: {
803
857
  currency: z6.string().describe("Project display currency (ISO-4217) for the revenue fields"),
@@ -818,7 +872,8 @@ function registerGoalTools(server, client) {
818
872
  })
819
873
  ).describe("Per-variant breakdown")
820
874
  })
821
- ).describe("Configured goals (empty if none)")
875
+ ).describe("Configured goals (empty if none)"),
876
+ window: windowOutputSchema
822
877
  },
823
878
  annotations: {
824
879
  readOnlyHint: true,
@@ -826,11 +881,12 @@ function registerGoalTools(server, client) {
826
881
  openWorldHint: false
827
882
  }
828
883
  },
829
- withApiErrorGuidance(async ({ projectId }) => {
884
+ withApiErrorGuidance(async ({ projectId, range, from, to }) => {
830
885
  var _a, _b;
831
886
  const id = encodeURIComponent(projectId);
832
- const data = await client.get(`/projects/${id}/goals`);
887
+ const data = await client.get(`/projects/${id}/goals${rangeQuery({ range, from, to })}`);
833
888
  const structuredContent = {
889
+ window: data.window,
834
890
  currency: (_a = data.currency) != null ? _a : "USD",
835
891
  goals: data.goals.map((g) => {
836
892
  var _a2, _b2, _c;
@@ -858,6 +914,7 @@ function registerGoalTools(server, client) {
858
914
  };
859
915
  }
860
916
  const currency = (_b = data.currency) != null ? _b : "USD";
917
+ const win = windowLine(data.window);
861
918
  const lines = data.goals.flatMap((g) => {
862
919
  var _a2;
863
920
  return [
@@ -866,7 +923,8 @@ function registerGoalTools(server, client) {
866
923
  ""
867
924
  ];
868
925
  });
869
- return { content: [{ type: "text", text: lines.join("\n").trim() }], structuredContent, _meta: uiMeta("goal-funnel") };
926
+ const text = (win ? [win, "", ...lines] : lines).join("\n").trim();
927
+ return { content: [{ type: "text", text }], structuredContent, _meta: uiMeta("goal-funnel") };
870
928
  })
871
929
  );
872
930
  server.registerTool(
@@ -1613,9 +1671,15 @@ function registerTestBriefTools(server, client) {
1613
1671
  import { z as z13 } from "zod";
1614
1672
  var GUIDE = `# SentientUI integration guide \u2014 the adaptive ladder
1615
1673
 
1616
- SentientUI adapts a site per visitor type (personas: buyer, researcher, deal_seeker, browser,
1617
- unknown), learning from real conversions. Decisions are locked per session: Visit 1 learns,
1618
- Visit 2 converts. Integrate one rung at a time.
1674
+ SentientUI adapts a site per visitor type (personas), learning from real conversions. Each
1675
+ project has its own persona vocabulary (dashboard -> Settings -> Personas; the default is
1676
+ buyer, researcher, deal_seeker, browser + unknown). Personas are inferred from behavior \u2014 or
1677
+ DECLARED by the app when it already knows the visitor's role: init({ persona: 'admin' }) /
1678
+ <AdaptiveProvider persona=...> / window.sentient.persona. Declared beats inferred and serves
1679
+ at full confidence; unrecognized values are ignored server-side and surfaced in the dashboard
1680
+ so the operator can add them. Keep persona a low-cardinality role slug \u2014 never a user id.
1681
+ Decisions are locked per session: Visit 1 learns, Visit 2 converts. Integrate one rung at a
1682
+ time.
1619
1683
 
1620
1684
  ## Setup (60 seconds, no account)
1621
1685
 
@@ -1633,7 +1697,8 @@ Visit 2 converts. Integrate one rung at a time.
1633
1697
 
1634
1698
  ## Rung 1 \u2014 Style (CSS only)
1635
1699
 
1636
- Persona attributes on <html> (zero declaration):
1700
+ Persona attributes on <html> (zero declaration; the attribute carries the project's own
1701
+ vocabulary keys \u2014 declared or inferred):
1637
1702
 
1638
1703
  html[data-sentient-persona='deal_seeker'] .discount-banner { display: block; }
1639
1704
  html[data-sentient-confidence='low'] .discount-banner { display: none; }
@@ -1710,8 +1775,12 @@ function registerAgentTrafficTools(server, client) {
1710
1775
  "get_agent_traffic",
1711
1776
  {
1712
1777
  title: "Agent traffic",
1713
- description: "Which AI agents and crawlers are reading this site: totals by type (passive crawlers, agentic browsers, agent API calls), engine breakdown, and the paths they fetch most. Agent traffic is tracked separately and never counted in conversion rate.",
1714
- inputSchema: { projectId: projectIdSchema },
1778
+ description: "Which AI agents and crawlers are reading this site: totals by type (passive crawlers, agentic browsers, agent API calls), engine breakdown, and the paths they fetch most. Defaults to the last 30 days; pass from/to for a custom period (agent logs are retained 30 days on Free/Starter, 90 on Growth+). Agent traffic is tracked separately and never counted in conversion rate.",
1779
+ inputSchema: {
1780
+ projectId: projectIdSchema,
1781
+ from: z14.string().optional().describe("Period start (ISO 8601 date or timestamp). Defaults to 30 days ago."),
1782
+ to: z14.string().optional().describe("Period end (ISO 8601 date or timestamp). Defaults to now.")
1783
+ },
1715
1784
  outputSchema: {
1716
1785
  totals: z14.object({ crawler: z14.number(), api: z14.number(), browser: z14.number() }),
1717
1786
  engines: z14.array(
@@ -1729,9 +1798,14 @@ function registerAgentTrafficTools(server, client) {
1729
1798
  },
1730
1799
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
1731
1800
  },
1732
- withApiErrorGuidance(async ({ projectId }) => {
1801
+ withApiErrorGuidance(async ({ projectId, from, to }) => {
1733
1802
  const id = encodeURIComponent(projectId);
1734
- const s = await client.get(`/projects/${id}/agent-activity/summary`);
1803
+ const qs = new URLSearchParams();
1804
+ if (from) qs.set("from", from);
1805
+ if (to) qs.set("to", to);
1806
+ const s = await client.get(
1807
+ `/projects/${id}/agent-activity/summary${qs.size > 0 ? `?${qs.toString()}` : ""}`
1808
+ );
1735
1809
  const structuredContent = { totals: s.totals, engines: s.engines, intents: s.intents, topPaths: s.topPaths };
1736
1810
  const total = s.totals.crawler + s.totals.api + s.totals.browser;
1737
1811
  if (total === 0) {
@@ -1819,7 +1893,7 @@ function registerAgentTrafficTools(server, client) {
1819
1893
  }
1820
1894
 
1821
1895
  // src/server.ts
1822
- var PKG_VERSION = true ? "0.16.0" : "0.0.0-dev";
1896
+ var PKG_VERSION = true ? "0.17.1" : "0.0.0-dev";
1823
1897
  function createMcpServer(client) {
1824
1898
  const server = new McpServer(
1825
1899
  {
package/dist/index.cjs CHANGED
@@ -78,6 +78,44 @@ var import_zod2 = require("zod");
78
78
  // src/tools/common.ts
79
79
  var import_zod = require("zod");
80
80
  var projectIdSchema = import_zod.z.string().uuid().describe("The project UUID");
81
+ function rangeInputSchema(defaultRange) {
82
+ return {
83
+ range: import_zod.z.enum(["7d", "30d", "90d", "all"]).optional().describe(
84
+ `Calendar window in the project's reporting timezone (default ${defaultRange}). 'all' means all retained data \u2014 the plan's retention window bounds every lookback.`
85
+ ),
86
+ from: import_zod.z.string().optional().describe(
87
+ "Custom window start (YYYY-MM-DD; overrides range). Must be within the plan retention window."
88
+ ),
89
+ to: import_zod.z.string().optional().describe("Custom window end (YYYY-MM-DD, inclusive; default today).")
90
+ };
91
+ }
92
+ function rangeQuery(args) {
93
+ const p = new URLSearchParams();
94
+ if (args.from) {
95
+ p.set("from", args.from);
96
+ if (args.to) p.set("to", args.to);
97
+ } else if (args.range) {
98
+ p.set("range", args.range);
99
+ }
100
+ const s = p.toString();
101
+ return s ? `?${s}` : "";
102
+ }
103
+ var windowOutputSchema = import_zod.z.object({
104
+ range: import_zod.z.string(),
105
+ start: import_zod.z.string().nullable(),
106
+ end: import_zod.z.string().nullable(),
107
+ timezone: import_zod.z.string(),
108
+ bucketUnit: import_zod.z.string(),
109
+ retentionDays: import_zod.z.number().describe("The plan's effective data retention in days"),
110
+ earliestAvailable: import_zod.z.string().describe("First date retention still holds data for (YYYY-MM-DD)")
111
+ }).optional().describe("The window the server actually used for every count in this response");
112
+ function windowLine(window) {
113
+ if (!window) return "";
114
+ if (window.start && window.end) {
115
+ return `Window: ${window.start.slice(0, 10)} to ${window.end.slice(0, 10)} (${window.timezone}, exclusive end)`;
116
+ }
117
+ return `Window: all retained data (${window.retentionDays}-day retention)`;
118
+ }
81
119
  function apiErrorGuidance(err, extra) {
82
120
  if (extra && Object.prototype.hasOwnProperty.call(extra, err.message)) {
83
121
  return extra[err.message];
@@ -92,6 +130,12 @@ function apiErrorGuidance(err, extra) {
92
130
  default:
93
131
  break;
94
132
  }
133
+ if (err.message === "range_out_of_retention") {
134
+ return "The requested window starts before this plan retains data \u2014 older events have been deleted by the retention purge and cannot be recovered. Ask for a more recent window, or upgrade the SentientUI plan for longer history (Growth keeps 180 days, Scale 365).";
135
+ }
136
+ if (err.message === "invalid_range") {
137
+ return "Invalid time window. Use range '7d', '30d', '90d' or 'all', or from/to as YYYY-MM-DD dates.";
138
+ }
95
139
  if (err.status === 402) {
96
140
  return `This feature requires a higher plan (${err.message}). Upgrade your SentientUI plan, then try again.`;
97
141
  }
@@ -393,7 +437,11 @@ var RENDER_JS = {
393
437
  "variant-performance": `
394
438
  window.__render = function(data){
395
439
  var variants = (data && data.variants) || [];
396
- el('sub').textContent = variants.length + ' variant' + (variants.length === 1 ? '' : 's') + ' \xB7 conversion, last 7 days vs prior 7';
440
+ var winText = 'current vs prior period';
441
+ if (data && data.window && data.window.start && data.window.end) {
442
+ winText = String(data.window.start).slice(0, 10) + ' to ' + String(data.window.end).slice(0, 10) + ' vs prior period';
443
+ }
444
+ el('sub').textContent = variants.length + ' variant' + (variants.length === 1 ? '' : 's') + ' \xB7 conversion, ' + winText;
397
445
  if (!variants.length) { showEmpty('No variant data available yet.'); return; }
398
446
  var max = variants.reduce(function(m,v){ return Math.max(m, v.currentCvr || 0); }, 0) || 1;
399
447
  var sorted = variants.slice().sort(function(a,b){ return (b.currentCvr||0) - (a.currentCvr||0); });
@@ -522,8 +570,8 @@ function registerComponentTools(server, client) {
522
570
  "list_components",
523
571
  {
524
572
  title: "List components",
525
- description: "List all adaptive components in a project with variant counts and impression totals.",
526
- inputSchema: { projectId: projectIdSchema },
573
+ description: "List all adaptive components in a project with variant counts and impression totals. Counts cover all retained data by default; pass range or from/to for a window.",
574
+ inputSchema: __spreadValues({ projectId: projectIdSchema }, rangeInputSchema("all")),
527
575
  outputSchema: {
528
576
  components: import_zod3.z.array(
529
577
  import_zod3.z.object({
@@ -532,7 +580,8 @@ function registerComponentTools(server, client) {
532
580
  impressions: import_zod3.z.number(),
533
581
  conversions: import_zod3.z.number()
534
582
  })
535
- ).describe("Adaptive components in the project (empty if none)")
583
+ ).describe("Adaptive components in the project (empty if none)"),
584
+ window: windowOutputSchema
536
585
  },
537
586
  annotations: {
538
587
  readOnlyHint: true,
@@ -540,16 +589,17 @@ function registerComponentTools(server, client) {
540
589
  openWorldHint: false
541
590
  }
542
591
  },
543
- withApiErrorGuidance(async ({ projectId }) => {
592
+ withApiErrorGuidance(async ({ projectId, range, from, to }) => {
544
593
  const id = encodeURIComponent(projectId);
545
- const { components } = await client.get(`/projects/${id}/components`);
594
+ const { components, window } = await client.get(`/projects/${id}/components${rangeQuery({ range, from, to })}`);
546
595
  const structuredContent = {
547
596
  components: components.map((c) => ({
548
597
  componentId: c.component_id,
549
598
  variantCount: c.variants.length,
550
599
  impressions: c.total_impressions,
551
600
  conversions: c.total_conversions
552
- }))
601
+ })),
602
+ window
553
603
  };
554
604
  if (!components.length) {
555
605
  return {
@@ -567,19 +617,20 @@ function registerComponentTools(server, client) {
567
617
  "get_variant_performance",
568
618
  {
569
619
  title: "Variant performance",
570
- description: "Get CVR and momentum for all variants in a project over the last 7 days vs prior 7 days.",
571
- inputSchema: { projectId: projectIdSchema },
620
+ description: "Get CVR and momentum for all variants in a project over the selected window vs the immediately-preceding window of equal length (default: last 7 calendar days vs prior 7).",
621
+ inputSchema: __spreadValues({ projectId: projectIdSchema }, rangeInputSchema("7d")),
572
622
  _meta: uiMeta("variant-performance"),
573
623
  outputSchema: {
574
624
  variants: import_zod3.z.array(
575
625
  import_zod3.z.object({
576
626
  variantId: import_zod3.z.string(),
577
- currentCvr: import_zod3.z.number().describe("Conversion rate over the last 7 days (0-1)"),
578
- priorCvr: import_zod3.z.number().describe("Conversion rate over the prior 7 days (0-1)"),
627
+ currentCvr: import_zod3.z.number().describe("Conversion rate over the selected window (0-1)"),
628
+ priorCvr: import_zod3.z.number().describe("Conversion rate over the preceding window (0-1)"),
579
629
  deltaPp: import_zod3.z.number().describe("Change in percentage points"),
580
630
  momentum: import_zod3.z.string().describe("Momentum direction: gaining, losing, or stable")
581
631
  })
582
- ).describe("Per-variant performance (empty if no data yet)")
632
+ ).describe("Per-variant performance (empty if no data yet)"),
633
+ window: windowOutputSchema
583
634
  },
584
635
  annotations: {
585
636
  readOnlyHint: true,
@@ -587,10 +638,10 @@ function registerComponentTools(server, client) {
587
638
  openWorldHint: false
588
639
  }
589
640
  },
590
- withApiErrorGuidance(async ({ projectId }) => {
641
+ withApiErrorGuidance(async ({ projectId, range, from, to }) => {
591
642
  var _a2, _b, _c;
592
643
  const id = encodeURIComponent(projectId);
593
- const data = await client.get(`/projects/${id}/trends`);
644
+ const data = await client.get(`/projects/${id}/trends${rangeQuery({ range, from, to })}`);
594
645
  const momentumMap = new Map(((_a2 = data.momentum) != null ? _a2 : []).map((m) => [m.variantId, m.direction]));
595
646
  const structuredContent = {
596
647
  variants: ((_b = data.cvr) != null ? _b : []).map((v) => {
@@ -602,7 +653,8 @@ function registerComponentTools(server, client) {
602
653
  deltaPp: v.deltaPp,
603
654
  momentum: (_a3 = momentumMap.get(v.variantId)) != null ? _a3 : "stable"
604
655
  };
605
- })
656
+ }),
657
+ window: data.window
606
658
  };
607
659
  if (!((_c = data.cvr) == null ? void 0 : _c.length)) {
608
660
  return {
@@ -611,12 +663,14 @@ function registerComponentTools(server, client) {
611
663
  _meta: uiMeta("variant-performance")
612
664
  };
613
665
  }
614
- const text = data.cvr.map(
666
+ const lines = data.cvr.map(
615
667
  (v) => {
616
668
  var _a3;
617
669
  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"})`;
618
670
  }
619
- ).join("\n");
671
+ );
672
+ const win = windowLine(data.window);
673
+ const text = (win ? [win, ...lines] : lines).join("\n");
620
674
  return { content: [{ type: "text", text }], structuredContent, _meta: uiMeta("variant-performance") };
621
675
  })
622
676
  );
@@ -802,8 +856,8 @@ function registerGoalTools(server, client) {
802
856
  "get_goal_funnel",
803
857
  {
804
858
  title: "Goal funnel",
805
- description: "Get goal hit counts, unique-session conversion rates, and per-variant breakdown. This is a flat per-goal list \u2014 for multi-step funnel drop-off, use get_funnel_report.",
806
- inputSchema: { projectId: projectIdSchema },
859
+ description: "Get goal hit counts, unique-session conversion rates, and per-variant breakdown (default window: last 30 calendar days). This is a flat per-goal list \u2014 for multi-step funnel drop-off, use get_funnel_report.",
860
+ inputSchema: __spreadValues({ projectId: projectIdSchema }, rangeInputSchema("30d")),
807
861
  _meta: uiMeta("goal-funnel"),
808
862
  outputSchema: {
809
863
  currency: import_zod6.z.string().describe("Project display currency (ISO-4217) for the revenue fields"),
@@ -824,7 +878,8 @@ function registerGoalTools(server, client) {
824
878
  })
825
879
  ).describe("Per-variant breakdown")
826
880
  })
827
- ).describe("Configured goals (empty if none)")
881
+ ).describe("Configured goals (empty if none)"),
882
+ window: windowOutputSchema
828
883
  },
829
884
  annotations: {
830
885
  readOnlyHint: true,
@@ -832,11 +887,12 @@ function registerGoalTools(server, client) {
832
887
  openWorldHint: false
833
888
  }
834
889
  },
835
- withApiErrorGuidance(async ({ projectId }) => {
890
+ withApiErrorGuidance(async ({ projectId, range, from, to }) => {
836
891
  var _a2, _b;
837
892
  const id = encodeURIComponent(projectId);
838
- const data = await client.get(`/projects/${id}/goals`);
893
+ const data = await client.get(`/projects/${id}/goals${rangeQuery({ range, from, to })}`);
839
894
  const structuredContent = {
895
+ window: data.window,
840
896
  currency: (_a2 = data.currency) != null ? _a2 : "USD",
841
897
  goals: data.goals.map((g) => {
842
898
  var _a3, _b2, _c;
@@ -864,6 +920,7 @@ function registerGoalTools(server, client) {
864
920
  };
865
921
  }
866
922
  const currency = (_b = data.currency) != null ? _b : "USD";
923
+ const win = windowLine(data.window);
867
924
  const lines = data.goals.flatMap((g) => {
868
925
  var _a3;
869
926
  return [
@@ -872,7 +929,8 @@ function registerGoalTools(server, client) {
872
929
  ""
873
930
  ];
874
931
  });
875
- return { content: [{ type: "text", text: lines.join("\n").trim() }], structuredContent, _meta: uiMeta("goal-funnel") };
932
+ const text = (win ? [win, "", ...lines] : lines).join("\n").trim();
933
+ return { content: [{ type: "text", text }], structuredContent, _meta: uiMeta("goal-funnel") };
876
934
  })
877
935
  );
878
936
  server.registerTool(
@@ -1619,9 +1677,15 @@ function registerTestBriefTools(server, client) {
1619
1677
  var import_zod13 = require("zod");
1620
1678
  var GUIDE = `# SentientUI integration guide \u2014 the adaptive ladder
1621
1679
 
1622
- SentientUI adapts a site per visitor type (personas: buyer, researcher, deal_seeker, browser,
1623
- unknown), learning from real conversions. Decisions are locked per session: Visit 1 learns,
1624
- Visit 2 converts. Integrate one rung at a time.
1680
+ SentientUI adapts a site per visitor type (personas), learning from real conversions. Each
1681
+ project has its own persona vocabulary (dashboard -> Settings -> Personas; the default is
1682
+ buyer, researcher, deal_seeker, browser + unknown). Personas are inferred from behavior \u2014 or
1683
+ DECLARED by the app when it already knows the visitor's role: init({ persona: 'admin' }) /
1684
+ <AdaptiveProvider persona=...> / window.sentient.persona. Declared beats inferred and serves
1685
+ at full confidence; unrecognized values are ignored server-side and surfaced in the dashboard
1686
+ so the operator can add them. Keep persona a low-cardinality role slug \u2014 never a user id.
1687
+ Decisions are locked per session: Visit 1 learns, Visit 2 converts. Integrate one rung at a
1688
+ time.
1625
1689
 
1626
1690
  ## Setup (60 seconds, no account)
1627
1691
 
@@ -1639,7 +1703,8 @@ Visit 2 converts. Integrate one rung at a time.
1639
1703
 
1640
1704
  ## Rung 1 \u2014 Style (CSS only)
1641
1705
 
1642
- Persona attributes on <html> (zero declaration):
1706
+ Persona attributes on <html> (zero declaration; the attribute carries the project's own
1707
+ vocabulary keys \u2014 declared or inferred):
1643
1708
 
1644
1709
  html[data-sentient-persona='deal_seeker'] .discount-banner { display: block; }
1645
1710
  html[data-sentient-confidence='low'] .discount-banner { display: none; }
@@ -1716,8 +1781,12 @@ function registerAgentTrafficTools(server, client) {
1716
1781
  "get_agent_traffic",
1717
1782
  {
1718
1783
  title: "Agent traffic",
1719
- description: "Which AI agents and crawlers are reading this site: totals by type (passive crawlers, agentic browsers, agent API calls), engine breakdown, and the paths they fetch most. Agent traffic is tracked separately and never counted in conversion rate.",
1720
- inputSchema: { projectId: projectIdSchema },
1784
+ description: "Which AI agents and crawlers are reading this site: totals by type (passive crawlers, agentic browsers, agent API calls), engine breakdown, and the paths they fetch most. Defaults to the last 30 days; pass from/to for a custom period (agent logs are retained 30 days on Free/Starter, 90 on Growth+). Agent traffic is tracked separately and never counted in conversion rate.",
1785
+ inputSchema: {
1786
+ projectId: projectIdSchema,
1787
+ from: import_zod14.z.string().optional().describe("Period start (ISO 8601 date or timestamp). Defaults to 30 days ago."),
1788
+ to: import_zod14.z.string().optional().describe("Period end (ISO 8601 date or timestamp). Defaults to now.")
1789
+ },
1721
1790
  outputSchema: {
1722
1791
  totals: import_zod14.z.object({ crawler: import_zod14.z.number(), api: import_zod14.z.number(), browser: import_zod14.z.number() }),
1723
1792
  engines: import_zod14.z.array(
@@ -1735,9 +1804,14 @@ function registerAgentTrafficTools(server, client) {
1735
1804
  },
1736
1805
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
1737
1806
  },
1738
- withApiErrorGuidance(async ({ projectId }) => {
1807
+ withApiErrorGuidance(async ({ projectId, from, to }) => {
1739
1808
  const id = encodeURIComponent(projectId);
1740
- const s = await client.get(`/projects/${id}/agent-activity/summary`);
1809
+ const qs = new URLSearchParams();
1810
+ if (from) qs.set("from", from);
1811
+ if (to) qs.set("to", to);
1812
+ const s = await client.get(
1813
+ `/projects/${id}/agent-activity/summary${qs.size > 0 ? `?${qs.toString()}` : ""}`
1814
+ );
1741
1815
  const structuredContent = { totals: s.totals, engines: s.engines, intents: s.intents, topPaths: s.topPaths };
1742
1816
  const total = s.totals.crawler + s.totals.api + s.totals.browser;
1743
1817
  if (total === 0) {
@@ -1825,7 +1899,7 @@ function registerAgentTrafficTools(server, client) {
1825
1899
  }
1826
1900
 
1827
1901
  // src/server.ts
1828
- var PKG_VERSION = true ? "0.16.0" : "0.0.0-dev";
1902
+ var PKG_VERSION = true ? "0.17.1" : "0.0.0-dev";
1829
1903
  function createMcpServer(client) {
1830
1904
  const server = new import_mcp.McpServer(
1831
1905
  {
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  ApiClient,
4
4
  createMcpServer
5
- } from "./chunk-SUMKFRQP.js";
5
+ } from "./chunk-KT2ZKW7I.js";
6
6
 
7
7
  // src/index.ts
8
8
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/dist/lib.cjs CHANGED
@@ -102,6 +102,44 @@ var ApiClient = class {
102
102
 
103
103
  // src/tools/common.ts
104
104
  var projectIdSchema = import_zod.z.string().uuid().describe("The project UUID");
105
+ function rangeInputSchema(defaultRange) {
106
+ return {
107
+ range: import_zod.z.enum(["7d", "30d", "90d", "all"]).optional().describe(
108
+ `Calendar window in the project's reporting timezone (default ${defaultRange}). 'all' means all retained data \u2014 the plan's retention window bounds every lookback.`
109
+ ),
110
+ from: import_zod.z.string().optional().describe(
111
+ "Custom window start (YYYY-MM-DD; overrides range). Must be within the plan retention window."
112
+ ),
113
+ to: import_zod.z.string().optional().describe("Custom window end (YYYY-MM-DD, inclusive; default today).")
114
+ };
115
+ }
116
+ function rangeQuery(args) {
117
+ const p = new URLSearchParams();
118
+ if (args.from) {
119
+ p.set("from", args.from);
120
+ if (args.to) p.set("to", args.to);
121
+ } else if (args.range) {
122
+ p.set("range", args.range);
123
+ }
124
+ const s = p.toString();
125
+ return s ? `?${s}` : "";
126
+ }
127
+ var windowOutputSchema = import_zod.z.object({
128
+ range: import_zod.z.string(),
129
+ start: import_zod.z.string().nullable(),
130
+ end: import_zod.z.string().nullable(),
131
+ timezone: import_zod.z.string(),
132
+ bucketUnit: import_zod.z.string(),
133
+ retentionDays: import_zod.z.number().describe("The plan's effective data retention in days"),
134
+ earliestAvailable: import_zod.z.string().describe("First date retention still holds data for (YYYY-MM-DD)")
135
+ }).optional().describe("The window the server actually used for every count in this response");
136
+ function windowLine(window) {
137
+ if (!window) return "";
138
+ if (window.start && window.end) {
139
+ return `Window: ${window.start.slice(0, 10)} to ${window.end.slice(0, 10)} (${window.timezone}, exclusive end)`;
140
+ }
141
+ return `Window: all retained data (${window.retentionDays}-day retention)`;
142
+ }
105
143
  function apiErrorGuidance(err, extra) {
106
144
  if (extra && Object.prototype.hasOwnProperty.call(extra, err.message)) {
107
145
  return extra[err.message];
@@ -116,6 +154,12 @@ function apiErrorGuidance(err, extra) {
116
154
  default:
117
155
  break;
118
156
  }
157
+ if (err.message === "range_out_of_retention") {
158
+ return "The requested window starts before this plan retains data \u2014 older events have been deleted by the retention purge and cannot be recovered. Ask for a more recent window, or upgrade the SentientUI plan for longer history (Growth keeps 180 days, Scale 365).";
159
+ }
160
+ if (err.message === "invalid_range") {
161
+ return "Invalid time window. Use range '7d', '30d', '90d' or 'all', or from/to as YYYY-MM-DD dates.";
162
+ }
119
163
  if (err.status === 402) {
120
164
  return `This feature requires a higher plan (${err.message}). Upgrade your SentientUI plan, then try again.`;
121
165
  }
@@ -417,7 +461,11 @@ var RENDER_JS = {
417
461
  "variant-performance": `
418
462
  window.__render = function(data){
419
463
  var variants = (data && data.variants) || [];
420
- el('sub').textContent = variants.length + ' variant' + (variants.length === 1 ? '' : 's') + ' \xB7 conversion, last 7 days vs prior 7';
464
+ var winText = 'current vs prior period';
465
+ if (data && data.window && data.window.start && data.window.end) {
466
+ winText = String(data.window.start).slice(0, 10) + ' to ' + String(data.window.end).slice(0, 10) + ' vs prior period';
467
+ }
468
+ el('sub').textContent = variants.length + ' variant' + (variants.length === 1 ? '' : 's') + ' \xB7 conversion, ' + winText;
421
469
  if (!variants.length) { showEmpty('No variant data available yet.'); return; }
422
470
  var max = variants.reduce(function(m,v){ return Math.max(m, v.currentCvr || 0); }, 0) || 1;
423
471
  var sorted = variants.slice().sort(function(a,b){ return (b.currentCvr||0) - (a.currentCvr||0); });
@@ -546,8 +594,8 @@ function registerComponentTools(server, client) {
546
594
  "list_components",
547
595
  {
548
596
  title: "List components",
549
- description: "List all adaptive components in a project with variant counts and impression totals.",
550
- inputSchema: { projectId: projectIdSchema },
597
+ description: "List all adaptive components in a project with variant counts and impression totals. Counts cover all retained data by default; pass range or from/to for a window.",
598
+ inputSchema: __spreadValues({ projectId: projectIdSchema }, rangeInputSchema("all")),
551
599
  outputSchema: {
552
600
  components: import_zod3.z.array(
553
601
  import_zod3.z.object({
@@ -556,7 +604,8 @@ function registerComponentTools(server, client) {
556
604
  impressions: import_zod3.z.number(),
557
605
  conversions: import_zod3.z.number()
558
606
  })
559
- ).describe("Adaptive components in the project (empty if none)")
607
+ ).describe("Adaptive components in the project (empty if none)"),
608
+ window: windowOutputSchema
560
609
  },
561
610
  annotations: {
562
611
  readOnlyHint: true,
@@ -564,16 +613,17 @@ function registerComponentTools(server, client) {
564
613
  openWorldHint: false
565
614
  }
566
615
  },
567
- withApiErrorGuidance(async ({ projectId }) => {
616
+ withApiErrorGuidance(async ({ projectId, range, from, to }) => {
568
617
  const id = encodeURIComponent(projectId);
569
- const { components } = await client.get(`/projects/${id}/components`);
618
+ const { components, window } = await client.get(`/projects/${id}/components${rangeQuery({ range, from, to })}`);
570
619
  const structuredContent = {
571
620
  components: components.map((c) => ({
572
621
  componentId: c.component_id,
573
622
  variantCount: c.variants.length,
574
623
  impressions: c.total_impressions,
575
624
  conversions: c.total_conversions
576
- }))
625
+ })),
626
+ window
577
627
  };
578
628
  if (!components.length) {
579
629
  return {
@@ -591,19 +641,20 @@ function registerComponentTools(server, client) {
591
641
  "get_variant_performance",
592
642
  {
593
643
  title: "Variant performance",
594
- description: "Get CVR and momentum for all variants in a project over the last 7 days vs prior 7 days.",
595
- inputSchema: { projectId: projectIdSchema },
644
+ description: "Get CVR and momentum for all variants in a project over the selected window vs the immediately-preceding window of equal length (default: last 7 calendar days vs prior 7).",
645
+ inputSchema: __spreadValues({ projectId: projectIdSchema }, rangeInputSchema("7d")),
596
646
  _meta: uiMeta("variant-performance"),
597
647
  outputSchema: {
598
648
  variants: import_zod3.z.array(
599
649
  import_zod3.z.object({
600
650
  variantId: import_zod3.z.string(),
601
- currentCvr: import_zod3.z.number().describe("Conversion rate over the last 7 days (0-1)"),
602
- priorCvr: import_zod3.z.number().describe("Conversion rate over the prior 7 days (0-1)"),
651
+ currentCvr: import_zod3.z.number().describe("Conversion rate over the selected window (0-1)"),
652
+ priorCvr: import_zod3.z.number().describe("Conversion rate over the preceding window (0-1)"),
603
653
  deltaPp: import_zod3.z.number().describe("Change in percentage points"),
604
654
  momentum: import_zod3.z.string().describe("Momentum direction: gaining, losing, or stable")
605
655
  })
606
- ).describe("Per-variant performance (empty if no data yet)")
656
+ ).describe("Per-variant performance (empty if no data yet)"),
657
+ window: windowOutputSchema
607
658
  },
608
659
  annotations: {
609
660
  readOnlyHint: true,
@@ -611,10 +662,10 @@ function registerComponentTools(server, client) {
611
662
  openWorldHint: false
612
663
  }
613
664
  },
614
- withApiErrorGuidance(async ({ projectId }) => {
665
+ withApiErrorGuidance(async ({ projectId, range, from, to }) => {
615
666
  var _a, _b, _c;
616
667
  const id = encodeURIComponent(projectId);
617
- const data = await client.get(`/projects/${id}/trends`);
668
+ const data = await client.get(`/projects/${id}/trends${rangeQuery({ range, from, to })}`);
618
669
  const momentumMap = new Map(((_a = data.momentum) != null ? _a : []).map((m) => [m.variantId, m.direction]));
619
670
  const structuredContent = {
620
671
  variants: ((_b = data.cvr) != null ? _b : []).map((v) => {
@@ -626,7 +677,8 @@ function registerComponentTools(server, client) {
626
677
  deltaPp: v.deltaPp,
627
678
  momentum: (_a2 = momentumMap.get(v.variantId)) != null ? _a2 : "stable"
628
679
  };
629
- })
680
+ }),
681
+ window: data.window
630
682
  };
631
683
  if (!((_c = data.cvr) == null ? void 0 : _c.length)) {
632
684
  return {
@@ -635,12 +687,14 @@ function registerComponentTools(server, client) {
635
687
  _meta: uiMeta("variant-performance")
636
688
  };
637
689
  }
638
- const text = data.cvr.map(
690
+ const lines = data.cvr.map(
639
691
  (v) => {
640
692
  var _a2;
641
693
  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"})`;
642
694
  }
643
- ).join("\n");
695
+ );
696
+ const win = windowLine(data.window);
697
+ const text = (win ? [win, ...lines] : lines).join("\n");
644
698
  return { content: [{ type: "text", text }], structuredContent, _meta: uiMeta("variant-performance") };
645
699
  })
646
700
  );
@@ -826,8 +880,8 @@ function registerGoalTools(server, client) {
826
880
  "get_goal_funnel",
827
881
  {
828
882
  title: "Goal funnel",
829
- description: "Get goal hit counts, unique-session conversion rates, and per-variant breakdown. This is a flat per-goal list \u2014 for multi-step funnel drop-off, use get_funnel_report.",
830
- inputSchema: { projectId: projectIdSchema },
883
+ description: "Get goal hit counts, unique-session conversion rates, and per-variant breakdown (default window: last 30 calendar days). This is a flat per-goal list \u2014 for multi-step funnel drop-off, use get_funnel_report.",
884
+ inputSchema: __spreadValues({ projectId: projectIdSchema }, rangeInputSchema("30d")),
831
885
  _meta: uiMeta("goal-funnel"),
832
886
  outputSchema: {
833
887
  currency: import_zod6.z.string().describe("Project display currency (ISO-4217) for the revenue fields"),
@@ -848,7 +902,8 @@ function registerGoalTools(server, client) {
848
902
  })
849
903
  ).describe("Per-variant breakdown")
850
904
  })
851
- ).describe("Configured goals (empty if none)")
905
+ ).describe("Configured goals (empty if none)"),
906
+ window: windowOutputSchema
852
907
  },
853
908
  annotations: {
854
909
  readOnlyHint: true,
@@ -856,11 +911,12 @@ function registerGoalTools(server, client) {
856
911
  openWorldHint: false
857
912
  }
858
913
  },
859
- withApiErrorGuidance(async ({ projectId }) => {
914
+ withApiErrorGuidance(async ({ projectId, range, from, to }) => {
860
915
  var _a, _b;
861
916
  const id = encodeURIComponent(projectId);
862
- const data = await client.get(`/projects/${id}/goals`);
917
+ const data = await client.get(`/projects/${id}/goals${rangeQuery({ range, from, to })}`);
863
918
  const structuredContent = {
919
+ window: data.window,
864
920
  currency: (_a = data.currency) != null ? _a : "USD",
865
921
  goals: data.goals.map((g) => {
866
922
  var _a2, _b2, _c;
@@ -888,6 +944,7 @@ function registerGoalTools(server, client) {
888
944
  };
889
945
  }
890
946
  const currency = (_b = data.currency) != null ? _b : "USD";
947
+ const win = windowLine(data.window);
891
948
  const lines = data.goals.flatMap((g) => {
892
949
  var _a2;
893
950
  return [
@@ -896,7 +953,8 @@ function registerGoalTools(server, client) {
896
953
  ""
897
954
  ];
898
955
  });
899
- return { content: [{ type: "text", text: lines.join("\n").trim() }], structuredContent, _meta: uiMeta("goal-funnel") };
956
+ const text = (win ? [win, "", ...lines] : lines).join("\n").trim();
957
+ return { content: [{ type: "text", text }], structuredContent, _meta: uiMeta("goal-funnel") };
900
958
  })
901
959
  );
902
960
  server.registerTool(
@@ -1643,9 +1701,15 @@ function registerTestBriefTools(server, client) {
1643
1701
  var import_zod13 = require("zod");
1644
1702
  var GUIDE = `# SentientUI integration guide \u2014 the adaptive ladder
1645
1703
 
1646
- SentientUI adapts a site per visitor type (personas: buyer, researcher, deal_seeker, browser,
1647
- unknown), learning from real conversions. Decisions are locked per session: Visit 1 learns,
1648
- Visit 2 converts. Integrate one rung at a time.
1704
+ SentientUI adapts a site per visitor type (personas), learning from real conversions. Each
1705
+ project has its own persona vocabulary (dashboard -> Settings -> Personas; the default is
1706
+ buyer, researcher, deal_seeker, browser + unknown). Personas are inferred from behavior \u2014 or
1707
+ DECLARED by the app when it already knows the visitor's role: init({ persona: 'admin' }) /
1708
+ <AdaptiveProvider persona=...> / window.sentient.persona. Declared beats inferred and serves
1709
+ at full confidence; unrecognized values are ignored server-side and surfaced in the dashboard
1710
+ so the operator can add them. Keep persona a low-cardinality role slug \u2014 never a user id.
1711
+ Decisions are locked per session: Visit 1 learns, Visit 2 converts. Integrate one rung at a
1712
+ time.
1649
1713
 
1650
1714
  ## Setup (60 seconds, no account)
1651
1715
 
@@ -1663,7 +1727,8 @@ Visit 2 converts. Integrate one rung at a time.
1663
1727
 
1664
1728
  ## Rung 1 \u2014 Style (CSS only)
1665
1729
 
1666
- Persona attributes on <html> (zero declaration):
1730
+ Persona attributes on <html> (zero declaration; the attribute carries the project's own
1731
+ vocabulary keys \u2014 declared or inferred):
1667
1732
 
1668
1733
  html[data-sentient-persona='deal_seeker'] .discount-banner { display: block; }
1669
1734
  html[data-sentient-confidence='low'] .discount-banner { display: none; }
@@ -1740,8 +1805,12 @@ function registerAgentTrafficTools(server, client) {
1740
1805
  "get_agent_traffic",
1741
1806
  {
1742
1807
  title: "Agent traffic",
1743
- description: "Which AI agents and crawlers are reading this site: totals by type (passive crawlers, agentic browsers, agent API calls), engine breakdown, and the paths they fetch most. Agent traffic is tracked separately and never counted in conversion rate.",
1744
- inputSchema: { projectId: projectIdSchema },
1808
+ description: "Which AI agents and crawlers are reading this site: totals by type (passive crawlers, agentic browsers, agent API calls), engine breakdown, and the paths they fetch most. Defaults to the last 30 days; pass from/to for a custom period (agent logs are retained 30 days on Free/Starter, 90 on Growth+). Agent traffic is tracked separately and never counted in conversion rate.",
1809
+ inputSchema: {
1810
+ projectId: projectIdSchema,
1811
+ from: import_zod14.z.string().optional().describe("Period start (ISO 8601 date or timestamp). Defaults to 30 days ago."),
1812
+ to: import_zod14.z.string().optional().describe("Period end (ISO 8601 date or timestamp). Defaults to now.")
1813
+ },
1745
1814
  outputSchema: {
1746
1815
  totals: import_zod14.z.object({ crawler: import_zod14.z.number(), api: import_zod14.z.number(), browser: import_zod14.z.number() }),
1747
1816
  engines: import_zod14.z.array(
@@ -1759,9 +1828,14 @@ function registerAgentTrafficTools(server, client) {
1759
1828
  },
1760
1829
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
1761
1830
  },
1762
- withApiErrorGuidance(async ({ projectId }) => {
1831
+ withApiErrorGuidance(async ({ projectId, from, to }) => {
1763
1832
  const id = encodeURIComponent(projectId);
1764
- const s = await client.get(`/projects/${id}/agent-activity/summary`);
1833
+ const qs = new URLSearchParams();
1834
+ if (from) qs.set("from", from);
1835
+ if (to) qs.set("to", to);
1836
+ const s = await client.get(
1837
+ `/projects/${id}/agent-activity/summary${qs.size > 0 ? `?${qs.toString()}` : ""}`
1838
+ );
1765
1839
  const structuredContent = { totals: s.totals, engines: s.engines, intents: s.intents, topPaths: s.topPaths };
1766
1840
  const total = s.totals.crawler + s.totals.api + s.totals.browser;
1767
1841
  if (total === 0) {
@@ -1849,7 +1923,7 @@ function registerAgentTrafficTools(server, client) {
1849
1923
  }
1850
1924
 
1851
1925
  // src/server.ts
1852
- var PKG_VERSION = true ? "0.16.0" : "0.0.0-dev";
1926
+ var PKG_VERSION = true ? "0.17.1" : "0.0.0-dev";
1853
1927
  function createMcpServer(client) {
1854
1928
  const server = new import_mcp.McpServer(
1855
1929
  {
package/dist/lib.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  ApiError,
5
5
  PUBLIC_UI_RESOURCE_URIS,
6
6
  createMcpServer
7
- } from "./chunk-SUMKFRQP.js";
7
+ } from "./chunk-KT2ZKW7I.js";
8
8
  export {
9
9
  ApiClient,
10
10
  ApiError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentientui/mcp",
3
- "version": "0.16.0",
3
+ "version": "0.17.1",
4
4
  "description": "MCP server for SentientUI — exposes project data and actions to AI agents",
5
5
  "license": "MIT",
6
6
  "homepage": "https://sentient-ui.com",