@neopress/mcp 1.5.0 → 1.6.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/README.md CHANGED
@@ -22,8 +22,13 @@ Stdio MCP can use `neopress_asset_upload_file` because it runs on the same
22
22
  machine as the agent and can read local file paths.
23
23
 
24
24
  The tool surface includes site/layout/page/collection/entry/form/asset/publish
25
- operations plus read-only analytics tools for traffic, forms, AI crawler reach,
26
- and Search Console.
25
+ operations plus analytics queries and saved event/funnel configuration. Use
26
+ `neopress_analytics_event_update` with `eventId` and `name` to rename an existing
27
+ event without changing its historical tracking behavior; display changes use a
28
+ separate call. Create a new event when tracking conditions need to change. Use
29
+ `neopress_analytics_targets_list` before creating an event to discover valid
30
+ page and form targets. Event and funnel creation accepts the same camelCase
31
+ definition inputs as the dashboard; reads preserve stored snake_case fields.
27
32
 
28
33
  ## Remote HTTP
29
34
 
@@ -178,3 +183,11 @@ pnpm dlx @anthropic-ai/mcpb pack packages/mcp/dist/mcpb/neopress
178
183
 
179
184
  Do not submit the remote connector drafts until the production MCP origin,
180
185
  support URL, review workspace, and real client OAuth scans are verified.
186
+
187
+ ## Localization
188
+
189
+ Page tools expose draft/production public languages; `draftPublicLocales: null` means only the site's default language and `[]` means no public languages. Publish the site to apply the draft list. Translation dictionaries and global page visibility are separate state.
190
+
191
+ `neopress_entry_create_translation` takes `entryId` and `locale`, returning an existing linked locale row or a source-content copy as a draft. It does not perform machine translation or publish content. Update and publish the returned entry separately. Collections share schemas with localized option labels; forms share schemas with flat `draftI18n.translations[textKey][locale]` dictionaries.
192
+
193
+ Partial update tools preserve omitted fields, including page/form i18n and metadata. Use explicit null only to clear nullable fields.
package/dist/bin.cjs CHANGED
@@ -19671,6 +19671,10 @@ var EntriesEndpoint = class {
19671
19671
  collectionId
19672
19672
  })).data;
19673
19673
  }
19674
+ /** Copy source content into a linked locale draft, or return its existing translation. */
19675
+ async createTranslation(entryId, locale) {
19676
+ return (await this.client.post(`${this.basePath}/${entryId}/translations`, { locale })).data;
19677
+ }
19674
19678
  async update(entryId, data) {
19675
19679
  const payload = {};
19676
19680
  for (const key of UPDATE_PASSTHROUGH_FIELDS) if (key in data) payload[key] = data[key];
@@ -19948,8 +19952,12 @@ function appendCommonAnalyticsParams(qs, params) {
19948
19952
  }
19949
19953
  var AnalyticsEndpoint = class {
19950
19954
  client;
19955
+ events;
19956
+ funnels;
19951
19957
  constructor(client) {
19952
19958
  this.client = client;
19959
+ this.events = new AnalyticsEventsEndpoint(client);
19960
+ this.funnels = new AnalyticsFunnelsEndpoint(client);
19953
19961
  }
19954
19962
  get basePath() {
19955
19963
  return `${this.client.siteBasePath}/analytics`;
@@ -20006,6 +20014,58 @@ var AnalyticsEndpoint = class {
20006
20014
  if (params.rowLimit) qs.set("row_limit", String(params.rowLimit));
20007
20015
  return (await this.client.get(`${this.basePath}/search-console?${qs.toString()}`)).data;
20008
20016
  }
20017
+ /** Page/form targets that can be referenced by an event definition. */
20018
+ async targets() {
20019
+ return (await this.client.get(`${this.basePath}/targets`)).data;
20020
+ }
20021
+ };
20022
+ var AnalyticsEventsEndpoint = class {
20023
+ client;
20024
+ constructor(client) {
20025
+ this.client = client;
20026
+ }
20027
+ get basePath() {
20028
+ return `${this.client.siteBasePath}/analytics/events`;
20029
+ }
20030
+ async list() {
20031
+ return (await this.client.get(this.basePath)).data;
20032
+ }
20033
+ async get(eventId) {
20034
+ return (await this.client.get(`${this.basePath}/${eventId}`)).data.event;
20035
+ }
20036
+ async create(data) {
20037
+ return (await this.client.post(this.basePath, data)).data.event;
20038
+ }
20039
+ async update(eventId, data) {
20040
+ return (await this.client.patch(`${this.basePath}/${eventId}`, data)).data.event;
20041
+ }
20042
+ async delete(eventId) {
20043
+ await this.client.delete(`${this.basePath}/${eventId}`);
20044
+ }
20045
+ };
20046
+ var AnalyticsFunnelsEndpoint = class {
20047
+ client;
20048
+ constructor(client) {
20049
+ this.client = client;
20050
+ }
20051
+ get basePath() {
20052
+ return `${this.client.siteBasePath}/analytics/funnels`;
20053
+ }
20054
+ async list() {
20055
+ return (await this.client.get(this.basePath)).data;
20056
+ }
20057
+ async get(funnelId) {
20058
+ return (await this.client.get(`${this.basePath}/${funnelId}`)).data.funnel;
20059
+ }
20060
+ async create(data) {
20061
+ return (await this.client.post(this.basePath, data)).data.funnel;
20062
+ }
20063
+ async update(funnelId, data) {
20064
+ return (await this.client.patch(`${this.basePath}/${funnelId}`, data)).data.funnel;
20065
+ }
20066
+ async delete(funnelId) {
20067
+ await this.client.delete(`${this.basePath}/${funnelId}`);
20068
+ }
20009
20069
  };
20010
20070
  //#endregion
20011
20071
  //#region ../sdk/src/endpoints/templates.ts
@@ -20731,6 +20791,11 @@ const READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
20731
20791
  "neopress_analytics_traffic_sources",
20732
20792
  "neopress_analytics_forms",
20733
20793
  "neopress_analytics_crawler_summary",
20794
+ "neopress_analytics_events_list",
20795
+ "neopress_analytics_event_get",
20796
+ "neopress_analytics_funnels_list",
20797
+ "neopress_analytics_funnel_get",
20798
+ "neopress_analytics_targets_list",
20734
20799
  "neopress_search_console_query",
20735
20800
  "neopress_assets_list",
20736
20801
  "neopress_asset_get",
@@ -20754,7 +20819,13 @@ const DESTRUCTIVE_TOOLS = /* @__PURE__ */ new Set([
20754
20819
  "neopress_form_delete",
20755
20820
  "neopress_asset_delete",
20756
20821
  "neopress_template_mark",
20757
- "neopress_publish_site"
20822
+ "neopress_publish_site",
20823
+ "neopress_analytics_event_create",
20824
+ "neopress_analytics_event_update",
20825
+ "neopress_analytics_event_delete",
20826
+ "neopress_analytics_funnel_create",
20827
+ "neopress_analytics_funnel_update",
20828
+ "neopress_analytics_funnel_delete"
20758
20829
  ]);
20759
20830
  const PUBLICATION_TOOLS = /* @__PURE__ */ new Set([
20760
20831
  "neopress_entry_publish",
@@ -20795,6 +20866,12 @@ function asOptionalNumber(input, key) {
20795
20866
  function asOptionalString(input, key) {
20796
20867
  return input[key];
20797
20868
  }
20869
+ function asOptionalBoolean(input, key) {
20870
+ return input[key];
20871
+ }
20872
+ function omitUndefined(value) {
20873
+ return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== void 0));
20874
+ }
20798
20875
  function siteId(input) {
20799
20876
  return asOptionalNumber(input, "siteId");
20800
20877
  }
@@ -21021,10 +21098,11 @@ const toolDefinitions = [
21021
21098
  title: string().optional(),
21022
21099
  type: _enum(["static", "dynamic"]).optional(),
21023
21100
  status: _enum(["draft", "published"]).optional(),
21101
+ visibility: _enum(["public", "hidden"]).optional(),
21024
21102
  collectionId: number().int().positive().optional(),
21025
21103
  draftTsx: string().optional(),
21026
21104
  draftI18n: optionalJsonSchema,
21027
- draftPublicLocales: array(string()).nullable().optional(),
21105
+ draftPublicLocales: array(string()).nullable().optional().describe("Draft public languages. null means default language only; [] means none. Publish to apply."),
21028
21106
  draftMeta: optionalJsonSchema,
21029
21107
  queryConfig: optionalJsonSchema,
21030
21108
  metadata: optionalJsonSchema
@@ -21035,6 +21113,7 @@ const toolDefinitions = [
21035
21113
  title: asOptionalString(input, "title"),
21036
21114
  type: input.type,
21037
21115
  status: input.status,
21116
+ visibility: input.visibility,
21038
21117
  collectionId: asOptionalNumber(input, "collectionId"),
21039
21118
  draftTsx: asOptionalString(input, "draftTsx"),
21040
21119
  draftI18n: input.draftI18n,
@@ -21055,27 +21134,29 @@ const toolDefinitions = [
21055
21134
  title: string().optional(),
21056
21135
  type: _enum(["static", "dynamic"]).optional(),
21057
21136
  status: string().optional(),
21137
+ visibility: _enum(["public", "hidden"]).optional(),
21058
21138
  collectionId: number().int().positive().optional(),
21059
21139
  draftTsx: string().optional(),
21060
21140
  draftI18n: optionalJsonSchema,
21061
- draftPublicLocales: array(string()).nullable().optional(),
21141
+ draftPublicLocales: array(string()).nullable().optional().describe("Draft public languages. null means default language only; [] means none. Publish to apply."),
21062
21142
  draftMeta: optionalJsonSchema,
21063
21143
  queryConfig: optionalJsonSchema,
21064
21144
  metadata: optionalJsonSchema
21065
21145
  },
21066
21146
  handler: async (input, context, extra) => {
21067
- return (await context.createClient({ siteId: siteId(input) }, extra)).pages.update(asNumber(input, "pageId"), {
21147
+ return (await context.createClient({ siteId: siteId(input) }, extra)).pages.update(asNumber(input, "pageId"), omitUndefined({
21068
21148
  path: asOptionalString(input, "path"),
21069
21149
  title: asOptionalString(input, "title"),
21070
21150
  type: asOptionalString(input, "type"),
21071
21151
  status: asOptionalString(input, "status"),
21152
+ visibility: input.visibility,
21072
21153
  collectionId: asOptionalNumber(input, "collectionId"),
21073
21154
  draftTsx: asOptionalString(input, "draftTsx"),
21074
21155
  draftI18n: input.draftI18n,
21075
21156
  draftPublicLocales: input.draftPublicLocales,
21076
21157
  draftMeta: mergeDraftMeta(input.draftMeta, input.queryConfig),
21077
21158
  metadata: input.metadata
21078
- });
21159
+ }));
21079
21160
  }
21080
21161
  },
21081
21162
  {
@@ -21231,6 +21312,19 @@ const toolDefinitions = [
21231
21312
  return entry;
21232
21313
  }
21233
21314
  },
21315
+ {
21316
+ name: "neopress_entry_create_translation",
21317
+ title: "Create Entry Translation Draft",
21318
+ 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.",
21319
+ inputSchema: {
21320
+ ...siteIdShape,
21321
+ entryId: number().int().positive(),
21322
+ locale: string().min(1)
21323
+ },
21324
+ handler: async (input, context, extra) => {
21325
+ return (await context.createClient({ siteId: siteId(input) }, extra)).entries.createTranslation(asNumber(input, "entryId"), asString(input, "locale"));
21326
+ }
21327
+ },
21234
21328
  {
21235
21329
  name: "neopress_entry_update",
21236
21330
  title: "Update Entry",
@@ -21247,14 +21341,14 @@ const toolDefinitions = [
21247
21341
  },
21248
21342
  handler: async (input, context, extra) => {
21249
21343
  const client = await context.createClient({ siteId: siteId(input) }, extra);
21250
- const entry = await client.entries.update(asNumber(input, "entryId"), {
21344
+ const entry = await client.entries.update(asNumber(input, "entryId"), omitUndefined({
21251
21345
  slug: asOptionalString(input, "slug"),
21252
21346
  title: asOptionalString(input, "title"),
21253
21347
  locale: asOptionalString(input, "locale"),
21254
21348
  data: input.data,
21255
21349
  metadata: input.metadata,
21256
21350
  status: asOptionalString(input, "status")
21257
- });
21351
+ }));
21258
21352
  await client.entries.revalidateReaderCache(asNumber(input, "entryId"));
21259
21353
  return entry;
21260
21354
  }
@@ -21419,14 +21513,14 @@ const toolDefinitions = [
21419
21513
  settings: optionalJsonSchema
21420
21514
  },
21421
21515
  handler: async (input, context, extra) => {
21422
- return (await context.createClient({ siteId: siteId(input) }, extra)).forms.update(asNumber(input, "formId"), {
21516
+ return (await context.createClient({ siteId: siteId(input) }, extra)).forms.update(asNumber(input, "formId"), omitUndefined({
21423
21517
  name: asOptionalString(input, "name"),
21424
- draftSchema: input.draftSchema ?? input.schema,
21518
+ draftSchema: input.draftSchema !== void 0 ? input.draftSchema : input.schema,
21425
21519
  draftLayout: input.draftLayout,
21426
21520
  draftStyle: input.draftStyle,
21427
21521
  draftI18n: input.draftI18n,
21428
21522
  settings: input.settings
21429
- });
21523
+ }));
21430
21524
  }
21431
21525
  },
21432
21526
  {
@@ -21470,6 +21564,235 @@ const toolDefinitions = [
21470
21564
  });
21471
21565
  }
21472
21566
  },
21567
+ {
21568
+ name: "neopress_analytics_events_list",
21569
+ title: "List Analytics Events",
21570
+ description: "List saved analytics event definitions for the active site.",
21571
+ inputSchema: siteIdShape,
21572
+ handler: async (input, context, extra) => {
21573
+ return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.events.list();
21574
+ }
21575
+ },
21576
+ {
21577
+ name: "neopress_analytics_event_get",
21578
+ title: "Get Analytics Event",
21579
+ description: "Get a saved analytics event definition by UUID.",
21580
+ inputSchema: {
21581
+ ...siteIdShape,
21582
+ eventId: string().uuid()
21583
+ },
21584
+ handler: async (input, context, extra) => {
21585
+ return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.events.get(asString(input, "eventId"));
21586
+ }
21587
+ },
21588
+ {
21589
+ name: "neopress_analytics_event_create",
21590
+ title: "Create Analytics Event",
21591
+ description: "Create a saved analytics event definition. Discover pages/forms with neopress_analytics_targets_list. Click/visibility require an existing stable [data-np-track=\"...\"] selector; page views reuse PAGE_VIEW. Optional id is a client UUID: reuse the same id and payload after an uncertain response.",
21592
+ inputSchema: {
21593
+ ...siteIdShape,
21594
+ id: string().uuid().optional(),
21595
+ name: string(),
21596
+ eventType: _enum([
21597
+ "custom",
21598
+ "page",
21599
+ "click",
21600
+ "form",
21601
+ "scroll",
21602
+ "visibility",
21603
+ "dwell"
21604
+ ]),
21605
+ eventName: string().optional(),
21606
+ path: string().optional(),
21607
+ selector: string().optional(),
21608
+ formId: string().optional(),
21609
+ scrollDepth: number().int().optional(),
21610
+ dwellSeconds: number().int().optional()
21611
+ },
21612
+ handler: async (input, context, extra) => {
21613
+ return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.events.create({
21614
+ id: asOptionalString(input, "id"),
21615
+ name: asString(input, "name"),
21616
+ eventType: input.eventType,
21617
+ eventName: asOptionalString(input, "eventName"),
21618
+ path: asOptionalString(input, "path"),
21619
+ selector: asOptionalString(input, "selector"),
21620
+ formId: asOptionalString(input, "formId"),
21621
+ scrollDepth: asOptionalNumber(input, "scrollDepth"),
21622
+ dwellSeconds: asOptionalNumber(input, "dwellSeconds")
21623
+ });
21624
+ }
21625
+ },
21626
+ {
21627
+ name: "neopress_analytics_event_update",
21628
+ title: "Update Analytics Event",
21629
+ description: "Rename an analytics event with {name} while preserving tracking behavior, or update its display settings separately. Tracking identity cannot be changed after creation.",
21630
+ inputSchema: {
21631
+ ...siteIdShape,
21632
+ eventId: string().uuid(),
21633
+ name: string().optional(),
21634
+ eventType: _enum([
21635
+ "custom",
21636
+ "page",
21637
+ "click",
21638
+ "form",
21639
+ "scroll",
21640
+ "visibility",
21641
+ "dwell"
21642
+ ]).optional(),
21643
+ eventName: string().optional(),
21644
+ path: string().optional(),
21645
+ selector: string().optional(),
21646
+ formId: string().optional(),
21647
+ scrollDepth: number().int().optional(),
21648
+ dwellSeconds: number().int().optional(),
21649
+ chartVisible: boolean().optional(),
21650
+ pinned: boolean().optional(),
21651
+ chartColor: string().optional()
21652
+ },
21653
+ handler: async (input, context, extra) => {
21654
+ const client = await context.createClient({ siteId: siteId(input) }, extra);
21655
+ if ([
21656
+ "chartVisible",
21657
+ "pinned",
21658
+ "chartColor"
21659
+ ].some((key) => key in input)) {
21660
+ if ([
21661
+ "name",
21662
+ "eventType",
21663
+ "eventName",
21664
+ "path",
21665
+ "selector",
21666
+ "formId",
21667
+ "scrollDepth",
21668
+ "dwellSeconds"
21669
+ ].some((key) => input[key] !== void 0)) throw new Error("Update the definition and display settings in separate calls.");
21670
+ return client.analytics.events.update(asString(input, "eventId"), { display: {
21671
+ chartVisible: asOptionalBoolean(input, "chartVisible"),
21672
+ pinned: asOptionalBoolean(input, "pinned"),
21673
+ chartColor: asOptionalString(input, "chartColor")
21674
+ } });
21675
+ }
21676
+ const definition = {
21677
+ name: asString(input, "name"),
21678
+ ...input.eventType !== void 0 ? { eventType: input.eventType } : {},
21679
+ eventName: asOptionalString(input, "eventName"),
21680
+ path: asOptionalString(input, "path"),
21681
+ selector: asOptionalString(input, "selector"),
21682
+ formId: asOptionalString(input, "formId"),
21683
+ scrollDepth: asOptionalNumber(input, "scrollDepth"),
21684
+ dwellSeconds: asOptionalNumber(input, "dwellSeconds")
21685
+ };
21686
+ return client.analytics.events.update(asString(input, "eventId"), definition);
21687
+ }
21688
+ },
21689
+ {
21690
+ name: "neopress_analytics_event_delete",
21691
+ title: "Delete Analytics Event",
21692
+ description: "Permanently delete a saved analytics event. Events referenced by a funnel cannot be deleted.",
21693
+ inputSchema: {
21694
+ ...siteIdShape,
21695
+ eventId: string().uuid()
21696
+ },
21697
+ handler: async (input, context, extra) => {
21698
+ await (await context.createClient({ siteId: siteId(input) }, extra)).analytics.events.delete(asString(input, "eventId"));
21699
+ return { ok: true };
21700
+ }
21701
+ },
21702
+ {
21703
+ name: "neopress_analytics_funnels_list",
21704
+ title: "List Analytics Funnels",
21705
+ description: "List saved analytics funnel definitions for the active site.",
21706
+ inputSchema: siteIdShape,
21707
+ handler: async (input, context, extra) => {
21708
+ return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.funnels.list();
21709
+ }
21710
+ },
21711
+ {
21712
+ name: "neopress_analytics_funnel_get",
21713
+ title: "Get Analytics Funnel",
21714
+ description: "Get a saved analytics funnel definition by UUID.",
21715
+ inputSchema: {
21716
+ ...siteIdShape,
21717
+ funnelId: string().uuid()
21718
+ },
21719
+ handler: async (input, context, extra) => {
21720
+ return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.funnels.get(asString(input, "funnelId"));
21721
+ }
21722
+ },
21723
+ {
21724
+ name: "neopress_analytics_funnel_create",
21725
+ title: "Create Analytics Funnel",
21726
+ 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.",
21727
+ inputSchema: {
21728
+ ...siteIdShape,
21729
+ id: string().uuid().optional(),
21730
+ name: string(),
21731
+ steps: array(object$1({
21732
+ eventId: string().uuid(),
21733
+ path: string().optional(),
21734
+ filter: unknown().optional()
21735
+ })),
21736
+ windowMode: _enum(["session", "time"]),
21737
+ windowHours: number().int().nonnegative().optional()
21738
+ },
21739
+ handler: async (input, context, extra) => {
21740
+ return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.funnels.create({
21741
+ id: asOptionalString(input, "id"),
21742
+ name: asString(input, "name"),
21743
+ steps: input.steps,
21744
+ windowMode: input.windowMode,
21745
+ windowHours: asOptionalNumber(input, "windowHours")
21746
+ });
21747
+ }
21748
+ },
21749
+ {
21750
+ name: "neopress_analytics_funnel_update",
21751
+ title: "Update Analytics Funnel",
21752
+ description: "Replace a saved funnel definition with its ordered event steps and window semantics.",
21753
+ inputSchema: {
21754
+ ...siteIdShape,
21755
+ funnelId: string().uuid(),
21756
+ name: string(),
21757
+ steps: array(object$1({
21758
+ eventId: string().uuid(),
21759
+ path: string().optional(),
21760
+ filter: unknown().optional()
21761
+ })),
21762
+ windowMode: _enum(["session", "time"]),
21763
+ windowHours: number().int().nonnegative().optional()
21764
+ },
21765
+ handler: async (input, context, extra) => {
21766
+ return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.funnels.update(asString(input, "funnelId"), {
21767
+ name: asString(input, "name"),
21768
+ steps: input.steps,
21769
+ windowMode: input.windowMode,
21770
+ windowHours: asOptionalNumber(input, "windowHours")
21771
+ });
21772
+ }
21773
+ },
21774
+ {
21775
+ name: "neopress_analytics_funnel_delete",
21776
+ title: "Delete Analytics Funnel",
21777
+ description: "Permanently delete a saved analytics funnel.",
21778
+ inputSchema: {
21779
+ ...siteIdShape,
21780
+ funnelId: string().uuid()
21781
+ },
21782
+ handler: async (input, context, extra) => {
21783
+ await (await context.createClient({ siteId: siteId(input) }, extra)).analytics.funnels.delete(asString(input, "funnelId"));
21784
+ return { ok: true };
21785
+ }
21786
+ },
21787
+ {
21788
+ name: "neopress_analytics_targets_list",
21789
+ title: "List Analytics Event Targets",
21790
+ description: "List site pages and forms available as analytics event targets.",
21791
+ inputSchema: siteIdShape,
21792
+ handler: async (input, context, extra) => {
21793
+ return (await context.createClient({ siteId: siteId(input) }, extra)).analytics.targets();
21794
+ }
21795
+ },
21473
21796
  {
21474
21797
  name: "neopress_analytics_query",
21475
21798
  title: "Query Analytics Pipe",