@neopress/mcp 1.5.0 → 1.6.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/README.md +15 -2
- package/dist/bin.cjs +344 -20
- package/dist/http-bin.cjs +344 -20
- package/dist/index.d.ts +131 -0
- package/dist/index.js +344 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -173,6 +173,10 @@ var EntriesEndpoint = class {
|
|
|
173
173
|
collectionId
|
|
174
174
|
})).data;
|
|
175
175
|
}
|
|
176
|
+
/** Copy source content into a linked locale draft, or return its existing translation. */
|
|
177
|
+
async createTranslation(entryId, locale) {
|
|
178
|
+
return (await this.client.post(`${this.basePath}/${entryId}/translations`, { locale })).data;
|
|
179
|
+
}
|
|
176
180
|
async update(entryId, data) {
|
|
177
181
|
const payload = {};
|
|
178
182
|
for (const key of UPDATE_PASSTHROUGH_FIELDS) if (key in data) payload[key] = data[key];
|
|
@@ -450,8 +454,12 @@ function appendCommonAnalyticsParams(qs, params) {
|
|
|
450
454
|
}
|
|
451
455
|
var AnalyticsEndpoint = class {
|
|
452
456
|
client;
|
|
457
|
+
events;
|
|
458
|
+
funnels;
|
|
453
459
|
constructor(client) {
|
|
454
460
|
this.client = client;
|
|
461
|
+
this.events = new AnalyticsEventsEndpoint(client);
|
|
462
|
+
this.funnels = new AnalyticsFunnelsEndpoint(client);
|
|
455
463
|
}
|
|
456
464
|
get basePath() {
|
|
457
465
|
return `${this.client.siteBasePath}/analytics`;
|
|
@@ -508,6 +516,58 @@ var AnalyticsEndpoint = class {
|
|
|
508
516
|
if (params.rowLimit) qs.set("row_limit", String(params.rowLimit));
|
|
509
517
|
return (await this.client.get(`${this.basePath}/search-console?${qs.toString()}`)).data;
|
|
510
518
|
}
|
|
519
|
+
/** Page/form targets that can be referenced by an event definition. */
|
|
520
|
+
async targets() {
|
|
521
|
+
return (await this.client.get(`${this.basePath}/targets`)).data;
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
var AnalyticsEventsEndpoint = class {
|
|
525
|
+
client;
|
|
526
|
+
constructor(client) {
|
|
527
|
+
this.client = client;
|
|
528
|
+
}
|
|
529
|
+
get basePath() {
|
|
530
|
+
return `${this.client.siteBasePath}/analytics/events`;
|
|
531
|
+
}
|
|
532
|
+
async list() {
|
|
533
|
+
return (await this.client.get(this.basePath)).data;
|
|
534
|
+
}
|
|
535
|
+
async get(eventId) {
|
|
536
|
+
return (await this.client.get(`${this.basePath}/${eventId}`)).data.event;
|
|
537
|
+
}
|
|
538
|
+
async create(data) {
|
|
539
|
+
return (await this.client.post(this.basePath, data)).data.event;
|
|
540
|
+
}
|
|
541
|
+
async update(eventId, data) {
|
|
542
|
+
return (await this.client.patch(`${this.basePath}/${eventId}`, data)).data.event;
|
|
543
|
+
}
|
|
544
|
+
async delete(eventId) {
|
|
545
|
+
await this.client.delete(`${this.basePath}/${eventId}`);
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
var AnalyticsFunnelsEndpoint = class {
|
|
549
|
+
client;
|
|
550
|
+
constructor(client) {
|
|
551
|
+
this.client = client;
|
|
552
|
+
}
|
|
553
|
+
get basePath() {
|
|
554
|
+
return `${this.client.siteBasePath}/analytics/funnels`;
|
|
555
|
+
}
|
|
556
|
+
async list() {
|
|
557
|
+
return (await this.client.get(this.basePath)).data;
|
|
558
|
+
}
|
|
559
|
+
async get(funnelId) {
|
|
560
|
+
return (await this.client.get(`${this.basePath}/${funnelId}`)).data.funnel;
|
|
561
|
+
}
|
|
562
|
+
async create(data) {
|
|
563
|
+
return (await this.client.post(this.basePath, data)).data.funnel;
|
|
564
|
+
}
|
|
565
|
+
async update(funnelId, data) {
|
|
566
|
+
return (await this.client.patch(`${this.basePath}/${funnelId}`, data)).data.funnel;
|
|
567
|
+
}
|
|
568
|
+
async delete(funnelId) {
|
|
569
|
+
await this.client.delete(`${this.basePath}/${funnelId}`);
|
|
570
|
+
}
|
|
511
571
|
};
|
|
512
572
|
//#endregion
|
|
513
573
|
//#region ../sdk/src/endpoints/templates.ts
|
|
@@ -863,9 +923,8 @@ Use these rules before generating pages, collection schemas, entries, forms, or
|
|
|
863
923
|
|
|
864
924
|
## Operating boundary
|
|
865
925
|
|
|
866
|
-
-
|
|
867
|
-
-
|
|
868
|
-
- MCP is available as local stdio and remote Streamable HTTP over the first-party SDK/API.
|
|
926
|
+
- Connect the remote MCP server and sign in with your Neopress account through OAuth.
|
|
927
|
+
- MCP is available on Launch and above. Your site role controls which actions you can perform.
|
|
869
928
|
- MCP is a Neopress contract adapter, not a reference crawler or managed page generator.
|
|
870
929
|
- The host coding agent should perform \`r.jina.ai\` markdown fetches, browser screenshots, DOM/CSS/SVG/background-image extraction, reference analysis, and TSX authoring with its own tools and tokens.
|
|
871
930
|
- Use MCP to read/write Neopress resources, upload or register assets, compile, publish, and verify. Do not expect MCP to spend Neopress server-side LLM tokens for reference analysis or page generation.
|
|
@@ -879,9 +938,9 @@ Use these rules before generating pages, collection schemas, entries, forms, or
|
|
|
879
938
|
|
|
880
939
|
## Cache
|
|
881
940
|
|
|
882
|
-
- Site publish invalidates site/layout/collection reader cache tags in the
|
|
941
|
+
- Site publish invalidates site/layout/collection reader cache tags in the environment connected to MCP.
|
|
883
942
|
- Entry publish/unpublish invalidates entry reader cache when production visibility changes.
|
|
884
|
-
- A localhost publish does not invalidate production reader cache. For production validation, publish through the production
|
|
943
|
+
- A localhost publish does not invalidate production reader cache. For production validation, publish through the production MCP server and verify the live URL.
|
|
885
944
|
|
|
886
945
|
## Analytics and autopilot
|
|
887
946
|
|
|
@@ -894,7 +953,7 @@ Use these rules before generating pages, collection schemas, entries, forms, or
|
|
|
894
953
|
|
|
895
954
|
- Layout owns header, footer, global CSS, and 404. Page TSX starts at page content.
|
|
896
955
|
- Generated page TSX is sandboxed. Do not import Supabase, create API clients, read env vars, use filesystem/process APIs, or assume arbitrary npm packages.
|
|
897
|
-
- Use allowed runtime packages only: React/Next basics, react-dom (createPortal), shadcn UI imports, lucide-react, @base-ui/react/drawer, @base-ui/react/dialog, @base-ui/react/accordion, @base-ui/react/switch, @base-ui/react/tabs, @base-ui/react/select, @neopress/youtube, @neopress/map, clsx, class-variance-authority, tailwind-merge, motion/react (never \`framer-motion\` — old package name, not on the allowlist), motion-plus/react (Motion+ premium components: Ticker, Carousel, Cursor, AnimateNumber, AnimateText, Typewriter, ScrambleText, Curtains), @paper-design/shaders-react, gsap (including \`gsap/ScrollTrigger\`, driven via \`@gsap/react\`'s \`useGSAP\`), react-hook-form, zod, @hookform/resolvers, @hookform/resolvers/zod, @neopress/content, @neopress/routing, @neopress/i18n, and @neopress/forms.
|
|
956
|
+
- Use allowed runtime packages only: React/Next basics, react-dom (createPortal), shadcn UI imports, lucide-react, @base-ui/react/drawer, @base-ui/react/dialog, @base-ui/react/accordion, @base-ui/react/switch, @base-ui/react/tabs, @base-ui/react/select, @neopress/youtube, @neopress/map, clsx, class-variance-authority, tailwind-merge, motion/react (never \`framer-motion\` — old package name, not on the allowlist), motion-plus/react (Motion+ premium components: Ticker, Carousel, Cursor, AnimateNumber, AnimateText, Typewriter, ScrambleText, Curtains), @paper-design/shaders-react, gsap (including \`gsap/ScrollTrigger\`, driven via \`@gsap/react\`'s \`useGSAP\`), react-hook-form, zod, @hookform/resolvers, @hookform/resolvers/zod, @neopress/content, @neopress/routing, @neopress/i18n, @neopress/responsive (MobileOnly with deferred desktop/mobile callbacks), and @neopress/forms.
|
|
898
957
|
- For dates, render an existing \`displayDate\` field directly when available. For raw timestamps, use \`Intl.DateTimeFormat\` or \`Date#toLocaleDateString\` with an explicit locale and \`timeZone\` so server rendering and hydration agree; use the site's required zone, or \`UTC\` when none is specified.
|
|
899
958
|
- @base-ui/react/select is an accessible listbox/dropdown (replaces a native \`<select>\`, whose popup is unstyled OS chrome). Compose its parts directly in page TSX; a \`<Select.Item value>\` is a string or the documented \`null\` sentinel (unlike Radix, an empty string is not banned); the \`Select.Popup\` portals to \`document.body\`, so give it an explicit z-index and an opaque background; match the trigger width with \`min-w-[var(--anchor-width)]\` (from \`Select.Positioner\`), cap the list with \`max-h-[var(--available-height)]\`, and set \`alignItemWithTrigger={false}\` for standard below-trigger placement. @base-ui/react/drawer is the gesture mobile sheet; @base-ui/react/dialog the non-gesture modal — both portal to \`document.body\`, need an opaque Popup background + z-index, and take \`modal\` for focus-trap.
|
|
900
959
|
- react-dom is for \`createPortal\` — portal full-viewport overlays (mobile drawer, modal) to \`document.body\` so a \`backdrop-filter\`/\`transform\` ancestor can't trap their \`position: fixed\` inside its box. ALWAYS render the portal behind an open-state guard (\`{open && createPortal(…, document.body)}\`): an unguarded \`document\` reference throws during SSR.
|
|
@@ -916,7 +975,9 @@ Use these rules before generating pages, collection schemas, entries, forms, or
|
|
|
916
975
|
- \`queryConfig\` is stored in page \`draftMeta.queryConfig\`.
|
|
917
976
|
- Query labels become React props. Entry query results are flattened, so use \`post.body\` instead of \`post.data.body\`.
|
|
918
977
|
- A query may include \`collectionWindow\` on an \`entries\` query for a dynamic detail route. Its label receives \`{ before, after, items }\`; each is a flattened-entry array around the current entry, and \`items\` combines both directions. Windows use \`published_at DESC, id DESC\`, allow 0–20 entries per direction, and may only declare that exact order. Use the exact configured label, never a guessed prop name. Optional \`matchFields\` compare raw anchor-snapshot fields only when the source field has a value.
|
|
919
|
-
- Initial CMS data arrives through those injected query props. \`entryClient\`, when injected in reader/preview for a site handle, is only a client-side auxiliary for load-more/filter/get/create interactions after the initial render; do not use it for initial render or server data loading.
|
|
978
|
+
- Initial CMS data arrives through those injected query props. \`entryClient\`, when injected in reader/preview for a site handle, is only a client-side auxiliary for load-more/filter/get/create interactions after the initial render; do not use it for initial render or server data loading. Its entries come back flattened like injected props, ordered \`published_at DESC, id DESC\`.
|
|
979
|
+
- Server-side binding is the default because injected props are server-rendered and crawlable; \`entryClient\` is for after-hydration needs only (large-collection load-more, a read that depends on visitor input such as search text or a chosen filter, or an explicit client-side request).
|
|
980
|
+
- Load-more pagination: give the list query a \`limit\` (first page injected server-side), then append \`entryClient.more(propLabel, { offset: shown.length, filters? })\` — the server re-runs that page's stored binding query from the offset, so the page restates nothing; \`filters\` is equality on scalar schema fields (\`{ category: "TECH" }\`), applied to the same published data. \`entryClient.list({ collectionId, offset, limit, filters? })\` is only for reads whose query depends on visitor input or that are too large to inject; a light, fixed set revealed on click is a bound query instead. Judge "light" by what the query injects into the page, not by how much the UI shows: a query without a \`limit\` ships every entry with every field. Without a \`limit\` a list query injects every entry, so paginate by slicing the prop instead.
|
|
920
981
|
- Use numeric \`collection_id\` filters. Runtime controls site, status, locale, deleted state, and published snapshots.
|
|
921
982
|
|
|
922
983
|
## Collections and entries
|
|
@@ -930,7 +991,7 @@ Use these rules before generating pages, collection schemas, entries, forms, or
|
|
|
930
991
|
|
|
931
992
|
- Reference fields require a schema field with \`type: "references"\` and target \`collection_id\`.
|
|
932
993
|
- Links live in \`entry_references\`, not only in raw entry \`data\`.
|
|
933
|
-
- \`includeReferences\` fetches arrays keyed by the includeReferences alias or field key, and parent entry \`id\` must be selected. With an alias, the alias prop remains an array of referenced entries. Without an alias, if the field key matches a raw numeric reference field in \`entry.data\`, flattening unwraps that array to one
|
|
994
|
+
- \`includeReferences\` fetches arrays keyed by the includeReferences alias or field key, and parent entry \`id\` must be selected. With an alias, the alias prop remains an array of referenced entries. Without an alias, if the field key matches a raw numeric reference field in \`entry.data\`, flattening unwraps that array to one object or null; references linked only through \`entry_references\` stay arrays. Every referenced entry is flattened like the parent (\`ref.<fieldId>\`; \`ref.data\` kept for backward compatibility), one level deep.
|
|
934
995
|
- After changing references, publish the source entry again.
|
|
935
996
|
|
|
936
997
|
## Forms
|
|
@@ -943,7 +1004,7 @@ Use these rules before generating pages, collection schemas, entries, forms, or
|
|
|
943
1004
|
## Unsupported visitor features
|
|
944
1005
|
|
|
945
1006
|
Generated Neopress pages do not support visitor login/accounts, member areas, carts, checkout, payments, subscriptions, or order management.`;
|
|
946
|
-
const SERVER_INSTRUCTIONS = `Use Neopress MCP tools to manage a selected Neopress site through
|
|
1007
|
+
const SERVER_INSTRUCTIONS = `Use Neopress MCP tools to manage a selected Neopress site through an authenticated MCP connection.
|
|
947
1008
|
|
|
948
1009
|
Before generating TSX or schema-backed pages, call neopress_get_runtime_contract or read neopress://runtime-contract.
|
|
949
1010
|
Use numeric IDs returned by tools. Publish entries separately from site publish. Use read-only analytics tools before autopilot recommendations. Verify production URLs after production publish.
|
|
@@ -1233,6 +1294,11 @@ const READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
|
1233
1294
|
"neopress_analytics_traffic_sources",
|
|
1234
1295
|
"neopress_analytics_forms",
|
|
1235
1296
|
"neopress_analytics_crawler_summary",
|
|
1297
|
+
"neopress_analytics_events_list",
|
|
1298
|
+
"neopress_analytics_event_get",
|
|
1299
|
+
"neopress_analytics_funnels_list",
|
|
1300
|
+
"neopress_analytics_funnel_get",
|
|
1301
|
+
"neopress_analytics_targets_list",
|
|
1236
1302
|
"neopress_search_console_query",
|
|
1237
1303
|
"neopress_assets_list",
|
|
1238
1304
|
"neopress_asset_get",
|
|
@@ -1256,7 +1322,13 @@ const DESTRUCTIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
1256
1322
|
"neopress_form_delete",
|
|
1257
1323
|
"neopress_asset_delete",
|
|
1258
1324
|
"neopress_template_mark",
|
|
1259
|
-
"neopress_publish_site"
|
|
1325
|
+
"neopress_publish_site",
|
|
1326
|
+
"neopress_analytics_event_create",
|
|
1327
|
+
"neopress_analytics_event_update",
|
|
1328
|
+
"neopress_analytics_event_delete",
|
|
1329
|
+
"neopress_analytics_funnel_create",
|
|
1330
|
+
"neopress_analytics_funnel_update",
|
|
1331
|
+
"neopress_analytics_funnel_delete"
|
|
1260
1332
|
]);
|
|
1261
1333
|
const PUBLICATION_TOOLS = /* @__PURE__ */ new Set([
|
|
1262
1334
|
"neopress_entry_publish",
|
|
@@ -1297,6 +1369,12 @@ function asOptionalNumber(input, key) {
|
|
|
1297
1369
|
function asOptionalString(input, key) {
|
|
1298
1370
|
return input[key];
|
|
1299
1371
|
}
|
|
1372
|
+
function asOptionalBoolean(input, key) {
|
|
1373
|
+
return input[key];
|
|
1374
|
+
}
|
|
1375
|
+
function omitUndefined(value) {
|
|
1376
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== void 0));
|
|
1377
|
+
}
|
|
1300
1378
|
function siteId(input) {
|
|
1301
1379
|
return asOptionalNumber(input, "siteId");
|
|
1302
1380
|
}
|
|
@@ -1349,7 +1427,7 @@ const toolDefinitions = [
|
|
|
1349
1427
|
{
|
|
1350
1428
|
name: "neopress_site_select",
|
|
1351
1429
|
title: "Select Active Site",
|
|
1352
|
-
description: "Verify access to a site and store it as the active site for future MCP
|
|
1430
|
+
description: "Verify access to a site and store it as the active site for future MCP operations.",
|
|
1353
1431
|
inputSchema: { siteId: z.number().int().positive() },
|
|
1354
1432
|
handler: async (input, context, extra) => {
|
|
1355
1433
|
const selectedSiteId = asNumber(input, "siteId");
|
|
@@ -1523,10 +1601,11 @@ const toolDefinitions = [
|
|
|
1523
1601
|
title: z.string().optional(),
|
|
1524
1602
|
type: z.enum(["static", "dynamic"]).optional(),
|
|
1525
1603
|
status: z.enum(["draft", "published"]).optional(),
|
|
1604
|
+
visibility: z.enum(["public", "hidden"]).optional(),
|
|
1526
1605
|
collectionId: z.number().int().positive().optional(),
|
|
1527
1606
|
draftTsx: z.string().optional(),
|
|
1528
1607
|
draftI18n: optionalJsonSchema,
|
|
1529
|
-
draftPublicLocales: z.array(z.string()).nullable().optional(),
|
|
1608
|
+
draftPublicLocales: z.array(z.string()).nullable().optional().describe("Draft public languages. null means default language only; [] means none. Publish to apply."),
|
|
1530
1609
|
draftMeta: optionalJsonSchema,
|
|
1531
1610
|
queryConfig: optionalJsonSchema,
|
|
1532
1611
|
metadata: optionalJsonSchema
|
|
@@ -1537,6 +1616,7 @@ const toolDefinitions = [
|
|
|
1537
1616
|
title: asOptionalString(input, "title"),
|
|
1538
1617
|
type: input.type,
|
|
1539
1618
|
status: input.status,
|
|
1619
|
+
visibility: input.visibility,
|
|
1540
1620
|
collectionId: asOptionalNumber(input, "collectionId"),
|
|
1541
1621
|
draftTsx: asOptionalString(input, "draftTsx"),
|
|
1542
1622
|
draftI18n: input.draftI18n,
|
|
@@ -1557,27 +1637,29 @@ const toolDefinitions = [
|
|
|
1557
1637
|
title: z.string().optional(),
|
|
1558
1638
|
type: z.enum(["static", "dynamic"]).optional(),
|
|
1559
1639
|
status: z.string().optional(),
|
|
1640
|
+
visibility: z.enum(["public", "hidden"]).optional(),
|
|
1560
1641
|
collectionId: z.number().int().positive().optional(),
|
|
1561
1642
|
draftTsx: z.string().optional(),
|
|
1562
1643
|
draftI18n: optionalJsonSchema,
|
|
1563
|
-
draftPublicLocales: z.array(z.string()).nullable().optional(),
|
|
1644
|
+
draftPublicLocales: z.array(z.string()).nullable().optional().describe("Draft public languages. null means default language only; [] means none. Publish to apply."),
|
|
1564
1645
|
draftMeta: optionalJsonSchema,
|
|
1565
1646
|
queryConfig: optionalJsonSchema,
|
|
1566
1647
|
metadata: optionalJsonSchema
|
|
1567
1648
|
},
|
|
1568
1649
|
handler: async (input, context, extra) => {
|
|
1569
|
-
return (await context.createClient({ siteId: siteId(input) }, extra)).pages.update(asNumber(input, "pageId"), {
|
|
1650
|
+
return (await context.createClient({ siteId: siteId(input) }, extra)).pages.update(asNumber(input, "pageId"), omitUndefined({
|
|
1570
1651
|
path: asOptionalString(input, "path"),
|
|
1571
1652
|
title: asOptionalString(input, "title"),
|
|
1572
1653
|
type: asOptionalString(input, "type"),
|
|
1573
1654
|
status: asOptionalString(input, "status"),
|
|
1655
|
+
visibility: input.visibility,
|
|
1574
1656
|
collectionId: asOptionalNumber(input, "collectionId"),
|
|
1575
1657
|
draftTsx: asOptionalString(input, "draftTsx"),
|
|
1576
1658
|
draftI18n: input.draftI18n,
|
|
1577
1659
|
draftPublicLocales: input.draftPublicLocales,
|
|
1578
1660
|
draftMeta: mergeDraftMeta(input.draftMeta, input.queryConfig),
|
|
1579
1661
|
metadata: input.metadata
|
|
1580
|
-
});
|
|
1662
|
+
}));
|
|
1581
1663
|
}
|
|
1582
1664
|
},
|
|
1583
1665
|
{
|
|
@@ -1733,6 +1815,19 @@ const toolDefinitions = [
|
|
|
1733
1815
|
return entry;
|
|
1734
1816
|
}
|
|
1735
1817
|
},
|
|
1818
|
+
{
|
|
1819
|
+
name: "neopress_entry_create_translation",
|
|
1820
|
+
title: "Create Entry Translation Draft",
|
|
1821
|
+
description: "Create or return a linked locale row in the source entry's translation group. New drafts copy source content; translate the returned entry with entry_update, then publish it. Existing translations are preserved.",
|
|
1822
|
+
inputSchema: {
|
|
1823
|
+
...siteIdShape,
|
|
1824
|
+
entryId: z.number().int().positive(),
|
|
1825
|
+
locale: z.string().min(1)
|
|
1826
|
+
},
|
|
1827
|
+
handler: async (input, context, extra) => {
|
|
1828
|
+
return (await context.createClient({ siteId: siteId(input) }, extra)).entries.createTranslation(asNumber(input, "entryId"), asString(input, "locale"));
|
|
1829
|
+
}
|
|
1830
|
+
},
|
|
1736
1831
|
{
|
|
1737
1832
|
name: "neopress_entry_update",
|
|
1738
1833
|
title: "Update Entry",
|
|
@@ -1749,14 +1844,14 @@ const toolDefinitions = [
|
|
|
1749
1844
|
},
|
|
1750
1845
|
handler: async (input, context, extra) => {
|
|
1751
1846
|
const client = await context.createClient({ siteId: siteId(input) }, extra);
|
|
1752
|
-
const entry = await client.entries.update(asNumber(input, "entryId"), {
|
|
1847
|
+
const entry = await client.entries.update(asNumber(input, "entryId"), omitUndefined({
|
|
1753
1848
|
slug: asOptionalString(input, "slug"),
|
|
1754
1849
|
title: asOptionalString(input, "title"),
|
|
1755
1850
|
locale: asOptionalString(input, "locale"),
|
|
1756
1851
|
data: input.data,
|
|
1757
1852
|
metadata: input.metadata,
|
|
1758
1853
|
status: asOptionalString(input, "status")
|
|
1759
|
-
});
|
|
1854
|
+
}));
|
|
1760
1855
|
await client.entries.revalidateReaderCache(asNumber(input, "entryId"));
|
|
1761
1856
|
return entry;
|
|
1762
1857
|
}
|
|
@@ -1921,14 +2016,14 @@ const toolDefinitions = [
|
|
|
1921
2016
|
settings: optionalJsonSchema
|
|
1922
2017
|
},
|
|
1923
2018
|
handler: async (input, context, extra) => {
|
|
1924
|
-
return (await context.createClient({ siteId: siteId(input) }, extra)).forms.update(asNumber(input, "formId"), {
|
|
2019
|
+
return (await context.createClient({ siteId: siteId(input) }, extra)).forms.update(asNumber(input, "formId"), omitUndefined({
|
|
1925
2020
|
name: asOptionalString(input, "name"),
|
|
1926
|
-
draftSchema: input.draftSchema
|
|
2021
|
+
draftSchema: input.draftSchema !== void 0 ? input.draftSchema : input.schema,
|
|
1927
2022
|
draftLayout: input.draftLayout,
|
|
1928
2023
|
draftStyle: input.draftStyle,
|
|
1929
2024
|
draftI18n: input.draftI18n,
|
|
1930
2025
|
settings: input.settings
|
|
1931
|
-
});
|
|
2026
|
+
}));
|
|
1932
2027
|
}
|
|
1933
2028
|
},
|
|
1934
2029
|
{
|
|
@@ -1972,6 +2067,235 @@ const toolDefinitions = [
|
|
|
1972
2067
|
});
|
|
1973
2068
|
}
|
|
1974
2069
|
},
|
|
2070
|
+
{
|
|
2071
|
+
name: "neopress_analytics_events_list",
|
|
2072
|
+
title: "List Analytics Events",
|
|
2073
|
+
description: "List saved analytics event definitions for the active site.",
|
|
2074
|
+
inputSchema: siteIdShape,
|
|
2075
|
+
handler: async (input, context, extra) => {
|
|
2076
|
+
return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.events.list();
|
|
2077
|
+
}
|
|
2078
|
+
},
|
|
2079
|
+
{
|
|
2080
|
+
name: "neopress_analytics_event_get",
|
|
2081
|
+
title: "Get Analytics Event",
|
|
2082
|
+
description: "Get a saved analytics event definition by UUID.",
|
|
2083
|
+
inputSchema: {
|
|
2084
|
+
...siteIdShape,
|
|
2085
|
+
eventId: z.string().uuid()
|
|
2086
|
+
},
|
|
2087
|
+
handler: async (input, context, extra) => {
|
|
2088
|
+
return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.events.get(asString(input, "eventId"));
|
|
2089
|
+
}
|
|
2090
|
+
},
|
|
2091
|
+
{
|
|
2092
|
+
name: "neopress_analytics_event_create",
|
|
2093
|
+
title: "Create Analytics Event",
|
|
2094
|
+
description: "Create a saved analytics event definition over raw analytics signals. Discover page and form IDs with neopress_analytics_targets_list. Click targets use an observed href:<absolute-url> or selector:<fingerprint> key; outbound counts external-link clicks site-wide. Built-in events start collecting without editing or republishing page TSX. Optional id is a client UUID: reuse the same id and payload after an uncertain response.",
|
|
2095
|
+
inputSchema: {
|
|
2096
|
+
...siteIdShape,
|
|
2097
|
+
id: z.string().uuid().optional(),
|
|
2098
|
+
name: z.string(),
|
|
2099
|
+
eventType: z.enum([
|
|
2100
|
+
"custom",
|
|
2101
|
+
"page",
|
|
2102
|
+
"click",
|
|
2103
|
+
"form",
|
|
2104
|
+
"scroll",
|
|
2105
|
+
"dwell",
|
|
2106
|
+
"outbound"
|
|
2107
|
+
]),
|
|
2108
|
+
eventName: z.string().optional(),
|
|
2109
|
+
path: z.string().optional(),
|
|
2110
|
+
selector: z.string().optional(),
|
|
2111
|
+
formId: z.string().optional(),
|
|
2112
|
+
scrollDepth: z.number().int().optional(),
|
|
2113
|
+
dwellSeconds: z.number().int().optional()
|
|
2114
|
+
},
|
|
2115
|
+
handler: async (input, context, extra) => {
|
|
2116
|
+
return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.events.create({
|
|
2117
|
+
id: asOptionalString(input, "id"),
|
|
2118
|
+
name: asString(input, "name"),
|
|
2119
|
+
eventType: input.eventType,
|
|
2120
|
+
eventName: asOptionalString(input, "eventName"),
|
|
2121
|
+
path: asOptionalString(input, "path"),
|
|
2122
|
+
selector: asOptionalString(input, "selector"),
|
|
2123
|
+
formId: asOptionalString(input, "formId"),
|
|
2124
|
+
scrollDepth: asOptionalNumber(input, "scrollDepth"),
|
|
2125
|
+
dwellSeconds: asOptionalNumber(input, "dwellSeconds")
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
2128
|
+
},
|
|
2129
|
+
{
|
|
2130
|
+
name: "neopress_analytics_event_update",
|
|
2131
|
+
title: "Update Analytics Event",
|
|
2132
|
+
description: "Rename an analytics event with {name} while preserving tracking behavior, or update its display settings separately. Tracking identity cannot be changed after creation.",
|
|
2133
|
+
inputSchema: {
|
|
2134
|
+
...siteIdShape,
|
|
2135
|
+
eventId: z.string().uuid(),
|
|
2136
|
+
name: z.string().optional(),
|
|
2137
|
+
eventType: z.enum([
|
|
2138
|
+
"custom",
|
|
2139
|
+
"page",
|
|
2140
|
+
"click",
|
|
2141
|
+
"form",
|
|
2142
|
+
"scroll",
|
|
2143
|
+
"dwell",
|
|
2144
|
+
"outbound"
|
|
2145
|
+
]).optional(),
|
|
2146
|
+
eventName: z.string().optional(),
|
|
2147
|
+
path: z.string().optional(),
|
|
2148
|
+
selector: z.string().optional(),
|
|
2149
|
+
formId: z.string().optional(),
|
|
2150
|
+
scrollDepth: z.number().int().optional(),
|
|
2151
|
+
dwellSeconds: z.number().int().optional(),
|
|
2152
|
+
chartVisible: z.boolean().optional(),
|
|
2153
|
+
pinned: z.boolean().optional(),
|
|
2154
|
+
chartColor: z.string().optional()
|
|
2155
|
+
},
|
|
2156
|
+
handler: async (input, context, extra) => {
|
|
2157
|
+
const client = await context.createClient({ siteId: siteId(input) }, extra);
|
|
2158
|
+
if ([
|
|
2159
|
+
"chartVisible",
|
|
2160
|
+
"pinned",
|
|
2161
|
+
"chartColor"
|
|
2162
|
+
].some((key) => key in input)) {
|
|
2163
|
+
if ([
|
|
2164
|
+
"name",
|
|
2165
|
+
"eventType",
|
|
2166
|
+
"eventName",
|
|
2167
|
+
"path",
|
|
2168
|
+
"selector",
|
|
2169
|
+
"formId",
|
|
2170
|
+
"scrollDepth",
|
|
2171
|
+
"dwellSeconds"
|
|
2172
|
+
].some((key) => input[key] !== void 0)) throw new Error("Update the definition and display settings in separate calls.");
|
|
2173
|
+
return client.analytics.events.update(asString(input, "eventId"), { display: {
|
|
2174
|
+
chartVisible: asOptionalBoolean(input, "chartVisible"),
|
|
2175
|
+
pinned: asOptionalBoolean(input, "pinned"),
|
|
2176
|
+
chartColor: asOptionalString(input, "chartColor")
|
|
2177
|
+
} });
|
|
2178
|
+
}
|
|
2179
|
+
const definition = {
|
|
2180
|
+
name: asString(input, "name"),
|
|
2181
|
+
...input.eventType !== void 0 ? { eventType: input.eventType } : {},
|
|
2182
|
+
eventName: asOptionalString(input, "eventName"),
|
|
2183
|
+
path: asOptionalString(input, "path"),
|
|
2184
|
+
selector: asOptionalString(input, "selector"),
|
|
2185
|
+
formId: asOptionalString(input, "formId"),
|
|
2186
|
+
scrollDepth: asOptionalNumber(input, "scrollDepth"),
|
|
2187
|
+
dwellSeconds: asOptionalNumber(input, "dwellSeconds")
|
|
2188
|
+
};
|
|
2189
|
+
return client.analytics.events.update(asString(input, "eventId"), definition);
|
|
2190
|
+
}
|
|
2191
|
+
},
|
|
2192
|
+
{
|
|
2193
|
+
name: "neopress_analytics_event_delete",
|
|
2194
|
+
title: "Delete Analytics Event",
|
|
2195
|
+
description: "Permanently delete a saved analytics event. Events referenced by a funnel cannot be deleted.",
|
|
2196
|
+
inputSchema: {
|
|
2197
|
+
...siteIdShape,
|
|
2198
|
+
eventId: z.string().uuid()
|
|
2199
|
+
},
|
|
2200
|
+
handler: async (input, context, extra) => {
|
|
2201
|
+
await (await context.createClient({ siteId: siteId(input) }, extra)).analytics.events.delete(asString(input, "eventId"));
|
|
2202
|
+
return { ok: true };
|
|
2203
|
+
}
|
|
2204
|
+
},
|
|
2205
|
+
{
|
|
2206
|
+
name: "neopress_analytics_funnels_list",
|
|
2207
|
+
title: "List Analytics Funnels",
|
|
2208
|
+
description: "List saved analytics funnel definitions for the active site.",
|
|
2209
|
+
inputSchema: siteIdShape,
|
|
2210
|
+
handler: async (input, context, extra) => {
|
|
2211
|
+
return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.funnels.list();
|
|
2212
|
+
}
|
|
2213
|
+
},
|
|
2214
|
+
{
|
|
2215
|
+
name: "neopress_analytics_funnel_get",
|
|
2216
|
+
title: "Get Analytics Funnel",
|
|
2217
|
+
description: "Get a saved analytics funnel definition by UUID.",
|
|
2218
|
+
inputSchema: {
|
|
2219
|
+
...siteIdShape,
|
|
2220
|
+
funnelId: z.string().uuid()
|
|
2221
|
+
},
|
|
2222
|
+
handler: async (input, context, extra) => {
|
|
2223
|
+
return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.funnels.get(asString(input, "funnelId"));
|
|
2224
|
+
}
|
|
2225
|
+
},
|
|
2226
|
+
{
|
|
2227
|
+
name: "neopress_analytics_funnel_create",
|
|
2228
|
+
title: "Create Analytics Funnel",
|
|
2229
|
+
description: "Create a funnel with 2 to 5 ordered saved event UUIDs. Optional id is a client UUID; reuse it and the same payload after an uncertain response. Step filter is {version:1,root:{kind:'group',op:'and'|'or',children:[groups or {kind:'condition',field,operator,value?,key?}]}}. Fields: event.path/url/referrer/referrer_source/property; session.referrer/referrer_source/traffic_category/utm_source/utm_medium/utm_campaign/utm_content/utm_term. event.property requires key. Operators: eq,neq,contains,not_contains,starts_with,ends_with,exists,not_exists; property also supports gt,gte,lt,lte. Server validates bounds and grouping.",
|
|
2230
|
+
inputSchema: {
|
|
2231
|
+
...siteIdShape,
|
|
2232
|
+
id: z.string().uuid().optional(),
|
|
2233
|
+
name: z.string(),
|
|
2234
|
+
steps: z.array(z.object({
|
|
2235
|
+
eventId: z.string().uuid(),
|
|
2236
|
+
path: z.string().optional(),
|
|
2237
|
+
filter: z.unknown().optional()
|
|
2238
|
+
})),
|
|
2239
|
+
windowMode: z.enum(["session", "time"]),
|
|
2240
|
+
windowHours: z.number().int().nonnegative().optional()
|
|
2241
|
+
},
|
|
2242
|
+
handler: async (input, context, extra) => {
|
|
2243
|
+
return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.funnels.create({
|
|
2244
|
+
id: asOptionalString(input, "id"),
|
|
2245
|
+
name: asString(input, "name"),
|
|
2246
|
+
steps: input.steps,
|
|
2247
|
+
windowMode: input.windowMode,
|
|
2248
|
+
windowHours: asOptionalNumber(input, "windowHours")
|
|
2249
|
+
});
|
|
2250
|
+
}
|
|
2251
|
+
},
|
|
2252
|
+
{
|
|
2253
|
+
name: "neopress_analytics_funnel_update",
|
|
2254
|
+
title: "Update Analytics Funnel",
|
|
2255
|
+
description: "Replace a saved funnel definition with its ordered event steps and window semantics.",
|
|
2256
|
+
inputSchema: {
|
|
2257
|
+
...siteIdShape,
|
|
2258
|
+
funnelId: z.string().uuid(),
|
|
2259
|
+
name: z.string(),
|
|
2260
|
+
steps: z.array(z.object({
|
|
2261
|
+
eventId: z.string().uuid(),
|
|
2262
|
+
path: z.string().optional(),
|
|
2263
|
+
filter: z.unknown().optional()
|
|
2264
|
+
})),
|
|
2265
|
+
windowMode: z.enum(["session", "time"]),
|
|
2266
|
+
windowHours: z.number().int().nonnegative().optional()
|
|
2267
|
+
},
|
|
2268
|
+
handler: async (input, context, extra) => {
|
|
2269
|
+
return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.funnels.update(asString(input, "funnelId"), {
|
|
2270
|
+
name: asString(input, "name"),
|
|
2271
|
+
steps: input.steps,
|
|
2272
|
+
windowMode: input.windowMode,
|
|
2273
|
+
windowHours: asOptionalNumber(input, "windowHours")
|
|
2274
|
+
});
|
|
2275
|
+
}
|
|
2276
|
+
},
|
|
2277
|
+
{
|
|
2278
|
+
name: "neopress_analytics_funnel_delete",
|
|
2279
|
+
title: "Delete Analytics Funnel",
|
|
2280
|
+
description: "Permanently delete a saved analytics funnel.",
|
|
2281
|
+
inputSchema: {
|
|
2282
|
+
...siteIdShape,
|
|
2283
|
+
funnelId: z.string().uuid()
|
|
2284
|
+
},
|
|
2285
|
+
handler: async (input, context, extra) => {
|
|
2286
|
+
await (await context.createClient({ siteId: siteId(input) }, extra)).analytics.funnels.delete(asString(input, "funnelId"));
|
|
2287
|
+
return { ok: true };
|
|
2288
|
+
}
|
|
2289
|
+
},
|
|
2290
|
+
{
|
|
2291
|
+
name: "neopress_analytics_targets_list",
|
|
2292
|
+
title: "List Analytics Event Targets",
|
|
2293
|
+
description: "List site pages and forms available as analytics event targets.",
|
|
2294
|
+
inputSchema: siteIdShape,
|
|
2295
|
+
handler: async (input, context, extra) => {
|
|
2296
|
+
return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.targets();
|
|
2297
|
+
}
|
|
2298
|
+
},
|
|
1975
2299
|
{
|
|
1976
2300
|
name: "neopress_analytics_query",
|
|
1977
2301
|
title: "Query Analytics Pipe",
|