@neopress/mcp 1.3.1 → 1.5.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/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { z } from "zod";
3
+ import { AddressInfo } from "node:net";
3
4
  import { Server } from "node:http";
5
+ import { Redis } from "@upstash/redis";
4
6
  import { ServerNotification, ServerRequest } from "@modelcontextprotocol/sdk/types.js";
5
7
  import { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
6
- import { AddressInfo } from "node:net";
7
8
  //#region ../sdk/src/types.d.ts
8
9
  /**
9
10
  * @neopress/sdk — First-party API types
@@ -25,7 +26,7 @@ interface PaginatedResponse<T> {
25
26
  pagination: PaginationMeta;
26
27
  }
27
28
  /** Sort direction for list endpoints that support opt-in created_at ordering. */
28
- type SortOrder = 'asc' | 'desc';
29
+ type SortOrder = "asc" | "desc";
29
30
  interface Site {
30
31
  id: number;
31
32
  name: string | null;
@@ -38,7 +39,7 @@ interface Site {
38
39
  localeConfig: {
39
40
  defaultLocale: string;
40
41
  locales: string[];
41
- localePrefix?: 'as-needed';
42
+ localePrefix?: "as-needed";
42
43
  } | null;
43
44
  createdAt: string | null;
44
45
  updatedAt: string | null;
@@ -50,6 +51,8 @@ interface Template {
50
51
  referenceUrl: string;
51
52
  previewImgUrls: string[] | null;
52
53
  previewVideoUrl: string | null;
54
+ /** Primary language of the template's content, as an ISO 639-1 code (ko/en/es). */
55
+ language: string | null;
53
56
  createdAt: string | null;
54
57
  }
55
58
  interface TemplateListItem {
@@ -60,6 +63,7 @@ interface TemplateListItem {
60
63
  referenceUrl: string;
61
64
  previewImgUrls: string[] | null;
62
65
  description: string | null;
66
+ language: string | null;
63
67
  createdAt: string;
64
68
  }
65
69
  interface Page {
@@ -172,7 +176,7 @@ interface RedirectRule {
172
176
  siteId: number;
173
177
  source: string;
174
178
  destination: string;
175
- mode: 'redirect' | 'proxy';
179
+ mode: "redirect" | "proxy";
176
180
  redirectType: number;
177
181
  includeChildren: boolean;
178
182
  preservePath: boolean;
@@ -258,7 +262,7 @@ interface AnalyticsDateRangeParams {
258
262
  limit?: number;
259
263
  params?: Record<string, AnalyticsQueryValue>;
260
264
  }
261
- type SearchConsoleDimension = 'query' | 'page' | 'country' | 'device';
265
+ type SearchConsoleDimension = "query" | "page" | "country" | "device";
262
266
  interface SearchConsoleQueryParams {
263
267
  dateFrom: string;
264
268
  dateTo: string;
@@ -290,7 +294,7 @@ interface SearchConsoleQueryResult {
290
294
  interface PageListParams {
291
295
  page?: number;
292
296
  limit?: number;
293
- status?: 'draft' | 'published';
297
+ status?: "draft" | "published";
294
298
  fields?: string[];
295
299
  /** When set, sort by created_at instead of path. */
296
300
  order?: SortOrder;
@@ -298,9 +302,9 @@ interface PageListParams {
298
302
  interface PageCreateParams {
299
303
  path: string;
300
304
  title?: string;
301
- type?: 'static' | 'dynamic';
302
- status?: 'draft' | 'published';
303
- visibility?: 'public' | 'hidden';
305
+ type?: "static" | "dynamic";
306
+ status?: "draft" | "published";
307
+ visibility?: "public" | "hidden";
304
308
  draftTsx?: string;
305
309
  draftI18n?: unknown;
306
310
  draftPublicLocales?: string[] | null;
@@ -313,7 +317,7 @@ interface PageUpdateParams {
313
317
  title?: string;
314
318
  type?: string;
315
319
  status?: string;
316
- visibility?: 'public' | 'hidden';
320
+ visibility?: string;
317
321
  draftTsx?: string;
318
322
  draftI18n?: unknown;
319
323
  draftPublicLocales?: string[] | null;
@@ -350,6 +354,12 @@ declare class CollectionsEndpoint {
350
354
  order?: SortOrder;
351
355
  }): Promise<Collection[]>;
352
356
  get(collectionId: number): Promise<Collection>;
357
+ /**
358
+ * Preset normalization (`normalizeCollectionPresetType` +
359
+ * `ensureCollectionPresetBaselineFields`) now runs server-side in
360
+ * `CollectionsApi.create`, which applies the identical pair in the identical
361
+ * order — the stored row is unchanged by the move to HTTP.
362
+ */
353
363
  create(data: CollectionCreateParams): Promise<Collection>;
354
364
  update(collectionId: number, data: Partial<CollectionCreateParams>): Promise<Collection>;
355
365
  delete(collectionId: number): Promise<void>;
@@ -358,10 +368,6 @@ declare class CollectionsEndpoint {
358
368
  draftSchema: unknown;
359
369
  prodSchema: unknown;
360
370
  }>;
361
- /**
362
- * Updates the collection's draft_schema directly in Supabase.
363
- * validateApiCollectionSchema (app-local) is skipped — CLI/MCP callers are trusted.
364
- */
365
371
  updateSchema(collectionId: number, schema: unknown): Promise<{
366
372
  collectionId: number;
367
373
  draftSchema: unknown;
@@ -373,7 +379,7 @@ declare class CollectionsEndpoint {
373
379
  interface EntryListParams {
374
380
  page?: number;
375
381
  limit?: number;
376
- status?: 'draft' | 'published' | 'archived';
382
+ status?: "draft" | "published" | "archived";
377
383
  locale?: string;
378
384
  /** created_at sort direction. Defaults to 'desc' (newest first). */
379
385
  order?: SortOrder;
@@ -397,6 +403,7 @@ interface EntryUpdateParams {
397
403
  declare class EntriesEndpoint {
398
404
  private readonly client;
399
405
  constructor(client: NeopressClient);
406
+ private get basePath();
400
407
  list(collectionId: number, params?: EntryListParams): Promise<PaginatedResponse<Entry>>;
401
408
  get(entryId: number): Promise<Entry>;
402
409
  create(collectionId: number, data: EntryCreateParams): Promise<Entry>;
@@ -487,7 +494,12 @@ declare class AssetsEndpoint {
487
494
  }): Promise<PaginatedResponse<Asset>>;
488
495
  get(assetId: number): Promise<Asset>;
489
496
  register(data: AssetRegisterParams): Promise<Asset>;
490
- update(assetId: number, data: Partial<Omit<AssetRegisterParams, 'assetUrl'>>): Promise<Asset>;
497
+ update(assetId: number, data: Partial<Omit<AssetRegisterParams, "assetUrl">>): Promise<Asset>;
498
+ /**
499
+ * Hard delete. The route additionally requires the caller to be the site
500
+ * owner, matching `assets_delete_policy` = `is_site_owner(site_id)` — the
501
+ * registry axes alone would have widened this (W7 mapping §3-B1).
502
+ */
491
503
  delete(assetId: number): Promise<void>;
492
504
  presign(data: AssetPresignParams): Promise<{
493
505
  presignedUrl: string;
@@ -498,6 +510,12 @@ declare class AssetsEndpoint {
498
510
  //#endregion
499
511
  //#region ../sdk/src/endpoints/site.d.ts
500
512
  interface SiteWithRole extends Site {
513
+ /** Project role of the caller on this site: "owner" | "admin" | "member". */
514
+ projectRole: string;
515
+ /**
516
+ * @deprecated Legacy site role derived from `projectRole` for backwards
517
+ * compatibility ("owner" | "admin" | "member"). Use `projectRole` instead.
518
+ */
501
519
  role: string;
502
520
  }
503
521
  interface SiteCreateParams {
@@ -505,18 +523,25 @@ interface SiteCreateParams {
505
523
  siteHandle?: string;
506
524
  prompt?: string;
507
525
  language?: string;
508
- placeholder?: boolean;
509
526
  }
510
527
  declare class SiteEndpoint {
511
528
  private readonly client;
512
529
  constructor(client: NeopressClient);
530
+ /**
531
+ * Global (not site-scoped): lists the caller's own memberships, so it does not
532
+ * hang off `siteBasePath`. The membership + soft-delete filters that RLS used
533
+ * to apply now live in the route.
534
+ */
513
535
  list(params?: {
514
536
  order?: SortOrder;
515
537
  }): Promise<SiteWithRole[]>;
516
538
  get(): Promise<Site>;
517
539
  create(data: SiteCreateParams): Promise<Site>;
518
- update(data: Partial<Pick<Site, 'name' | 'metadata' | 'localeConfig'>> & {
519
- globalCss?: string;
540
+ /**
541
+ * Site settings only. The stylesheet is not here — it lives on the layout
542
+ * (`updateLayout({ draftGlobalCss })`), which is what the reader renders.
543
+ */
544
+ update(data: Partial<Pick<Site, "name" | "metadata" | "localeConfig">> & {
520
545
  instructions?: unknown;
521
546
  }): Promise<Site>;
522
547
  getLayout(): Promise<SiteLayout>;
@@ -544,11 +569,17 @@ interface RedirectCreateParams {
544
569
  declare class RedirectsEndpoint {
545
570
  private readonly client;
546
571
  constructor(client: NeopressClient);
572
+ private get basePath();
547
573
  list(params?: {
548
574
  order?: SortOrder;
549
575
  }): Promise<RedirectRule[]>;
550
576
  create(data: RedirectCreateParams): Promise<RedirectRule>;
551
- private assertRedirectRuleWritable;
577
+ /**
578
+ * The proxy preflight that used to run here (`assertRedirectRuleWritable`)
579
+ * needs a DB read, so it moved into the route. Its two error codes —
580
+ * `REDIRECT_RULE_NOT_FOUND` and `REDIRECT_RULE_DASHBOARD_ONLY` — are returned
581
+ * by the server verbatim, so `NeopressApiError.code` is unchanged for callers.
582
+ */
552
583
  update(ruleId: number, data: Partial<RedirectCreateParams>): Promise<RedirectRule>;
553
584
  delete(ruleId: number): Promise<void>;
554
585
  }
@@ -576,6 +607,7 @@ interface EntryReferenceCreateParams {
576
607
  declare class EntryReferencesEndpoint {
577
608
  private readonly client;
578
609
  constructor(client: NeopressClient);
610
+ private get basePath();
579
611
  list(params?: EntryReferenceListParams): Promise<EntryReference[]>;
580
612
  create(data: EntryReferenceCreateParams): Promise<EntryReference>;
581
613
  delete(referenceId: number): Promise<void>;
@@ -606,6 +638,11 @@ interface TemplateMarkParams {
606
638
  * card cross-fades from poster to video, so the poster must be the video's
607
639
  * first frame. Omit to leave an existing value untouched; `null` clears it. */
608
640
  previewVideoUrl?: string | null;
641
+ /** Primary language of the template's content — required, ISO 639-1 lowercase
642
+ * two-letter code (`ko`, `en`, `es`). Region-qualified locales (`en-US`) are
643
+ * rejected: the gallery filters on this key and must not see one language
644
+ * under two spellings. */
645
+ language: string;
609
646
  }
610
647
  interface TemplateDeleteResult {
611
648
  siteId: number;
@@ -621,9 +658,8 @@ declare class TemplatesEndpoint {
621
658
  /**
622
659
  * List templates across the sites the caller can access.
623
660
  *
624
- * Direct-DB: `templates` is public-read and `sites` RLS scopes the visible set
625
- * (own active memberships, or all sites for the super admin), so no server
626
- * route is needed — the two RLS-scoped queries reproduce the old handler.
661
+ * Global (not site-scoped) — the accessible-site set is resolved server-side,
662
+ * where RLS used to do it.
627
663
  */
628
664
  list(params?: {
629
665
  namePrefix?: string;
@@ -633,10 +669,10 @@ declare class TemplatesEndpoint {
633
669
  }
634
670
  //#endregion
635
671
  //#region ../sdk/src/endpoints/headshots.d.ts
636
- type HeadshotAgeBand = 'young' | 'middle';
637
- type HeadshotGender = 'male' | 'female';
638
- type HeadshotEthnicity = 'asian' | 'white' | 'black' | 'latino';
639
- type HeadshotAttire = 'formal' | 'casual';
672
+ type HeadshotAgeBand = "young" | "middle";
673
+ type HeadshotGender = "male" | "female";
674
+ type HeadshotEthnicity = "asian" | "white" | "black" | "latino";
675
+ type HeadshotAttire = "formal" | "casual";
640
676
  interface HeadshotListParams {
641
677
  page?: number;
642
678
  limit?: number;
@@ -685,9 +721,11 @@ interface HeadshotDeleteResult {
685
721
  deleted: true;
686
722
  }
687
723
  /**
688
- * headshot_pool is a GLOBAL (not site-scoped) admin table. RLS exposes it only to
689
- * the super-admin user, so these direct-DB calls return empty/forbidden for any
690
- * other account. No site id is used.
724
+ * `headshot_pool` is a GLOBAL (not site-scoped) admin table, so these calls run
725
+ * over `/api/v1/headshots` — no `siteId` in the path — instead of the
726
+ * site-scoped `withAuth` routes (W7 T5). The routes gate on the super-admin
727
+ * user id, which is the same check the table's single RLS policy made; a
728
+ * non-super-admin now gets 403 where the direct-DB read returned an empty list.
691
729
  */
692
730
  declare class HeadshotsEndpoint {
693
731
  private readonly client;
@@ -700,8 +738,8 @@ declare class HeadshotsEndpoint {
700
738
  }
701
739
  //#endregion
702
740
  //#region ../sdk/src/endpoints/logos.d.ts
703
- type LogoType = 'wordmark' | 'lockup' | 'signature';
704
- type LogoTone = 'playful' | 'neutral' | 'serious';
741
+ type LogoType = "wordmark" | "lockup" | "signature";
742
+ type LogoTone = "playful" | "neutral" | "serious";
705
743
  interface LogoListParams {
706
744
  page?: number;
707
745
  limit?: number;
@@ -756,9 +794,11 @@ interface LogoDeleteResult {
756
794
  deleted: true;
757
795
  }
758
796
  /**
759
- * logo_pool is a GLOBAL (not site-scoped) admin table. RLS exposes it only to the
760
- * super-admin user, so these direct-DB calls return empty/forbidden for any other
761
- * account. No site id is used.
797
+ * `logo_pool` is a GLOBAL (not site-scoped) admin table, so these calls run over
798
+ * `/api/v1/logos` — no `siteId` in the path — instead of the site-scoped
799
+ * `withAuth` routes (W7 T5). The routes gate on the super-admin user id, which
800
+ * is the same check the table's single RLS policy made; a non-super-admin now
801
+ * gets 403 where the direct-DB read returned an empty list.
762
802
  */
763
803
  declare class LogosEndpoint {
764
804
  private readonly client;
@@ -771,28 +811,29 @@ declare class LogosEndpoint {
771
811
  }
772
812
  //#endregion
773
813
  //#region ../sdk/src/client.d.ts
814
+ /**
815
+ * ⚠️ No `supabaseUrl` / `supabaseAnonKey`. This client speaks HTTP to
816
+ * `/api/v1/**` and nothing else — the direct-DB escape hatch (`client.db`) was
817
+ * removed in W7 so the seven tables it wrote to could have their
818
+ * `authenticated` write grants revoked
819
+ * (`docs/plan/2026-08-17-w7-sdk-retirement.md`). `check-dist-contract.mjs` in
820
+ * `@neopress/cli` / `@neopress/mcp` fails the build if either option returns.
821
+ */
774
822
  interface NeopressClientOptions {
775
823
  /** OAuth access token */
776
824
  accessToken?: string;
777
825
  /** Site ID to operate on */
778
826
  siteId: number | string;
779
- /** Base URL (default: https://app.neopress.ai) — used by orchestration/analytics (HTTP) routes */
827
+ /** Base URL (default: https://app.neopress.ai) */
780
828
  baseUrl?: string;
781
829
  /** Request timeout in ms (default: 30000) */
782
830
  timeout?: number;
783
- /** Supabase project URL — enables direct-DB reads/CRUD (transparent routing). */
784
- supabaseUrl?: string;
785
- /** Supabase anon key (paired with the user accessToken so RLS applies). */
786
- supabaseAnonKey?: string;
787
831
  }
788
832
  declare class NeopressClient {
789
833
  private readonly baseUrl;
790
834
  private readonly accessToken;
791
835
  private readonly siteId;
792
836
  private readonly timeout;
793
- private readonly supabaseUrl?;
794
- private readonly supabaseAnonKey?;
795
- private _db;
796
837
  readonly pages: PagesEndpoint;
797
838
  readonly collections: CollectionsEndpoint;
798
839
  readonly entries: EntriesEndpoint;
@@ -808,26 +849,6 @@ declare class NeopressClient {
808
849
  readonly logos: LogosEndpoint;
809
850
  constructor(options: NeopressClientOptions);
810
851
  get siteBasePath(): string;
811
- /** Numeric site id for direct DB queries. */
812
- get siteIdNumber(): number;
813
- /**
814
- * Authenticated user's profile id, decoded locally from the access token's
815
- * JWT `sub` claim (no network round-trip). Returns null if there is no token
816
- * or it cannot be parsed. Subsequent DB queries are RLS-protected, so a local
817
- * decode is sufficient — no server-side validation is required here.
818
- */
819
- get authUserId(): string | null;
820
- /** Mirror of v1 parsePagination(defaultLimit=20, maxLimit=100). */
821
- resolvePagination(params?: {
822
- page?: number;
823
- limit?: number;
824
- }): {
825
- page: number;
826
- limit: number;
827
- offset: number;
828
- };
829
- /** Build the paginated envelope matching the v1 API. */
830
- paginationMeta(page: number, limit: number, offset: number, totalCount: number): PaginationMeta;
831
852
  request<T>(method: string, path: string, body?: unknown): Promise<T>;
832
853
  get<T>(path: string): Promise<ApiResponse<T>>;
833
854
  getList<T>(path: string): Promise<PaginatedResponse<T>>;
@@ -850,7 +871,7 @@ interface ToolContext {
850
871
  size: number;
851
872
  };
852
873
  fetch: typeof fetch;
853
- setActiveSiteId(siteId: number, extra?: ToolRequestExtra): void;
874
+ setActiveSiteId(siteId: number, extra?: ToolRequestExtra): Promise<void>;
854
875
  getBaseUrl(): string;
855
876
  }
856
877
  interface NeopressToolDefinition {
@@ -875,8 +896,8 @@ declare function createNeopressMcpServer(options?: NeopressMcpServerOptions): Mc
875
896
  //#endregion
876
897
  //#region src/remote/context.d.ts
877
898
  interface RemoteSiteSelectionStore {
878
- get(userKey: string): number | undefined;
879
- set(userKey: string, siteId: number): void;
899
+ get(userKey: string): Promise<number | undefined>;
900
+ set(userKey: string, siteId: number): Promise<void>;
880
901
  }
881
902
  interface RemoteToolContextOptions {
882
903
  baseUrl: string;
@@ -886,15 +907,14 @@ declare function createMemoryRemoteSiteSelectionStore(): RemoteSiteSelectionStor
886
907
  declare function createRemoteToolContext(options: RemoteToolContextOptions): ToolContext;
887
908
  //#endregion
888
909
  //#region src/remote/http-server.d.ts
889
- type RemoteMcpAuthMode = 'oauth' | 'none';
910
+ type RemoteMcpAuthMode = "oauth" | "none";
890
911
  interface NeopressMcpHttpServerOptions {
891
912
  authMode?: RemoteMcpAuthMode;
892
913
  baseUrl?: string;
893
- corsOrigins?: string[] | '*';
914
+ corsOrigins?: string[] | "*";
894
915
  fetch?: typeof fetch;
895
916
  host?: string;
896
- oauthClientSecret?: string;
897
- oauthProvider?: string;
917
+ openaiAppsChallengeToken?: string;
898
918
  port?: number;
899
919
  publicUrl?: string;
900
920
  siteSelectionStore?: RemoteSiteSelectionStore;