@prenta/mcp-server 0.23.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/CHANGELOG.md ADDED
@@ -0,0 +1,583 @@
1
+ # @actuate-media/mcp-server
2
+
3
+ ## 0.23.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 5eea995: Product rename: ActuateCMS is now **Prenta**. Every package moves from the
8
+ `@actuate-media/*` scope to `@prenta/*` with simplified names (`@prenta/core`,
9
+ `@prenta/admin`, `create-prenta`), and version numbers continue from where the
10
+ old scope left off.
11
+
12
+ This is a clean break — no backwards-compat aliases:
13
+ - **npm packages**: `@actuate-media/cms-core` → `@prenta/core`,
14
+ `@actuate-media/cms-admin` → `@prenta/admin`, all other packages keep their
15
+ suffix under `@prenta/*`. `create-actuate-cms` → `create-prenta`
16
+ (`npm create prenta`), `create-actuate-astro` → `create-prenta-astro`.
17
+ CLI bins: `actuate`/`actuate-cms` → `prenta`/`prenta-cms`; `actuate-mcp` →
18
+ `prenta-mcp`.
19
+ - **Database**: migration `0012_rename_to_prenta` renames all 36 `actuate_*`
20
+ tables to `prenta_*` in place (plus prefixed constraints, indexes, and
21
+ sequences). Run `prisma migrate deploy` when upgrading.
22
+ - **Runtime identifiers**: `getActuateConfig` → `getPrentaConfig`,
23
+ `handleActuateAPI` → `handlePrentaAPI`, `ActuateClient` → `PrentaClient`,
24
+ `ActuateDB` → `PrentaDB`, config file `actuate.config.ts` →
25
+ `prenta.config.ts`, CSS scopes `.actuate-admin`/`.actuate-canvas` →
26
+ `.prenta-admin`/`.prenta-canvas`, preview protocol `actuate-preview:*` →
27
+ `prenta-preview:*`, webhook headers `X-Actuate-*` → `X-Prenta-*`, session
28
+ cookie `actuate_session` → `prenta_session`, env vars `ACTUATE_*` →
29
+ `PRENTA_*`, purge route `/api/actuate/purge` → `/api/prenta/purge`.
30
+ - **Sessions and webhooks**: all sessions reset on upgrade (cookie + JWT issuer
31
+ changed); webhook consumers must re-verify signatures against the new
32
+ `X-Prenta-*` headers.
33
+
34
+ The old `@actuate-media/*` packages are deprecated on npm and will receive no
35
+ further releases.
36
+
37
+ ## 0.22.2
38
+
39
+ ### Patch Changes
40
+
41
+ - cf97a40: Launch-critical Astro SEO + freshness: `documentPublicPath` / `rewritePublicOrigin`, first-class `AstroPageMeta.robots`, admin/MCP path builders (#739), Yoast child sitemap + XSL proxies with origin rewrite, production purge 503 without CDN credentials, globals purge webhooks, and Launch Gate docs.
42
+
43
+ ## 0.22.1
44
+
45
+ ### Patch Changes
46
+
47
+ - 46ea4a7: Hash final stored media bytes (SHA-256) on upload and return the existing record with `deduplicated` / `duplicateOf` when a match exists, so seed re-runs and byte-identical twins no longer accumulate. Opt out with `forceUpload`.
48
+
49
+ ## 0.22.0
50
+
51
+ ### Minor Changes
52
+
53
+ - e4e55de: Add `POST /media/bulk-update` and `POST /media/bulk-delete` with per-id results (max 50), plus MCP tools `bulk_update_media` and `bulk_delete_media` for library-wide metadata backfills without one round trip per asset.
54
+
55
+ ## 0.21.2
56
+
57
+ ### Patch Changes
58
+
59
+ - 6eaffa5: Treat decorative media (`isDecorative` + empty alt) as alt-complete in the media SEO scorer, add a non-scoring `low-quality-alt` advisory, deep-merge media fields on document update so partial `featuredImage` patches keep `url`/`id`, and expose `isDecorative` on `update_media`.
60
+
61
+ ## 0.21.1
62
+
63
+ ### Patch Changes
64
+
65
+ - 0d0c5c4: Expose `description`, `caption`, and `focusKeyword` on the `update_media` MCP tool. The media API already accepted all three, but the tool omitted them, so an agent could only reach 60 of 100 on `calculateMediaSeoScore` (alt 30 + title 20 + descriptive filename 10) and every asset it touched was stuck at a "needs-work" grade. The description alone is worth the 20 points that lift an asset to a passing "good".
66
+
67
+ ## 0.21.0
68
+
69
+ ### Minor Changes
70
+
71
+ - 258cbc3: Warn when navigation items link to unpublished or missing internal pages (soft warnings in MCP `validate_navigation_tree`, admin nav builder, and `POST /navigations/validate-targets`).
72
+
73
+ ## 0.20.2
74
+
75
+ ### Patch Changes
76
+
77
+ - cc6ad93: Split-deploy SEO redirects: ship `applyPublicRedirect` / `createAstroRedirectMiddleware`, wire Astro starters and Next scaffold to the shared engine, clarify Never hit vs Stale, and return 409 on duplicate source updates.
78
+
79
+ ## 0.20.1
80
+
81
+ ### Patch Changes
82
+
83
+ - 041a228: Add the MIT `license` field to every published package (previously missing) and correct `repository.url` to `github.com/actuate-media/actuatecms` (13 packages carried the wrong org).
84
+
85
+ ## 0.20.0
86
+
87
+ ### Minor Changes
88
+
89
+ - 55f52ed: Add Phase 1 SEO MCP parity tools: Links dashboard (snapshot, health, suggest, AI index), technical settings (crawl, robots, sitemap), redirect advanced ops (flatten, recovery, import/export), content SEO CRUD, and `/ai/seo/*` assistant endpoints.
90
+
91
+ ## 0.19.0
92
+
93
+ ### Minor Changes
94
+
95
+ - 4afb589: WCAG-aligned AI alt text: improved vision prompts, feature-gated `/ai/media-metadata` with `aiAltText` suggestions, post-upload auto-suggest in Media, MCP `generate_media_alt` tool, and validation warnings for static image paths outside the media library.
96
+
97
+ ### Patch Changes
98
+
99
+ - 4afb589: Media upload optimization: high-quality **lossy WebP** by default (q82 / q86 for transparent PNGs) for strong compression with minimal visible quality loss. Lossless WebP available via `optimizeImage({ lossless: true })`. Original bytes kept when WebP would not reduce file size.
100
+
101
+ ## 0.18.0
102
+
103
+ ### Minor Changes
104
+
105
+ - 39e2419: Phase 1–2: inline SEO audit fixes (deterministic + AI approve). Phase A: markdown export of manual/content fixes for dev-team handoff. Phase B: AI content briefs for thin content, heading, and alt-text issues. Phase C: topic research subagent, research-and-brief flow, and batch brief pre-generation. MCP: seven new tools for the audit fix workflow (`export_seo_fix_brief`, suggest/apply fix, content briefs, research-and-brief, batch generate) plus server instructions.
106
+
107
+ ## 0.17.1
108
+
109
+ ### Patch Changes
110
+
111
+ - c656ddc: Add an `embed` (video) section type for YouTube, Vimeo, and Loom.
112
+ - **cms-core:** new `embed` section in the registry (`url`, `title`, `ratio`, `caption`) plus a shared, deterministic `parseEmbed()` provider allowlist (`@actuate-media/cms-core/page-sections`). Section validation rejects any URL that isn't a recognised YouTube/Vimeo/Loom link, so an arbitrary URL can never reach an iframe.
113
+ - **sections-react:** `EmbedSection` renderer — a lazy-loaded provider iframe inside a fixed aspect-ratio box (16:9 / 4:3 / 1:1 / 9:16) so the embed reserves its space and never causes layout shift (CLS). The URL is re-parsed through the same allowlist at render time.
114
+ - **cms-admin:** matching editor-canvas body for `embed` (provider + ratio placeholder) so the page/post editors preview the section instead of showing a generic "custom section" placeholder.
115
+ - **mcp-server:** `list_section_types` description now lists `embed`.
116
+
117
+ ## 0.17.0
118
+
119
+ ### Minor Changes
120
+
121
+ - e45b9cd: Teach AI agents to build fast, Core Web Vitals-friendly pages over MCP.
122
+
123
+ The server `instructions` and the `actuate://guide` resource now include an "Images & performance" section: put a page's main banner on the `hero` section's `imageUrl` (or a post's `featuredImage`) so it becomes the page's single LCP image, always set `imageAlt`, and rely on the pipeline (WebP/AVIF via `next/image`, dimension backfill for zero CLS, responsive `sizes`) instead of hand-optimising. The live `actuate://section-types` resource already exposes the new hero image fields. Docs (`mcp.md`, `media.md`) updated to match.
124
+
125
+ Also corrects the server `instructions` safety rail to list the full set of tools that require `confirm: true` (adds `export_form_entries` and `seo_scan_run` alongside the destructive `delete_*` tools).
126
+
127
+ ## 0.16.2
128
+
129
+ ### Patch Changes
130
+
131
+ - 854152d: Developer experience improvements from external-consumer feedback:
132
+ - Every published package now ships its `CHANGELOG.md` in the npm tarball, and packages that were missing a `README.md` (cli, cms-admin, cms-core, import, platform-vercel, all plugins, sections-react) now have one — so release notes and usage docs are visible on npmjs.com and in `node_modules`.
133
+ - New client-safe form validation entry point: `@actuate-media/cms-core/forms/client` exports `validateFormFieldClient()`, `validateFormValuesClient()`, visibility/conditional helpers, and the exact `FORM_EMAIL_RE` / `FORM_PHONE_RE` regexes the server enforces — zero Node dependencies, so public-site forms can mirror server rules without reimplementing them. The server validator now imports the same primitives, so client and server can never drift.
134
+
135
+ ## 0.16.1
136
+
137
+ ### Patch Changes
138
+
139
+ - aa4bd1a: Teach agents the page-ownership rule (ADR 0002): server `instructions` and the `actuate://guide` resource now explain that a page with a `_layout` is Page-Builder-owned — its `sections` are compiled from the layout and the section write tools refuse it with a 409, so agents should read the compiled sections via `get_document` rather than retrying.
140
+
141
+ ## 0.16.0
142
+
143
+ ### Minor Changes
144
+
145
+ - 127f91a: Make the MCP server self-teaching so agents can drive the CMS out of the box.
146
+ - **Server `instructions`** — the server now returns onboarding guidance during
147
+ MCP `initialize` (the discovery-first workflow, the canonical `sections[]`
148
+ page-content model, the publish lifecycle, and the destructive-tool safety
149
+ rails). Most MCP clients inject this into the model's system context.
150
+ - **MCP resources** — three pull-on-demand resources: `actuate://guide`
151
+ (authoring guide, markdown), `actuate://collections` (the **live** content
152
+ model for the connected instance), and `actuate://section-types` (the **live**
153
+ registered section types and schemas). Resource reads never throw — a failed
154
+ discovery returns an actionable payload.
155
+
156
+ New exports: `buildResources`, `AUTHORING_GUIDE`, `SERVER_INSTRUCTIONS`, and the
157
+ `McpResource` type. `createActuateMcpServer` now also returns `resources`.
158
+
159
+ ## 0.15.0
160
+
161
+ ### Minor Changes
162
+
163
+ - 01eef99: Require explicit confirmation for destructive delete tools.
164
+
165
+ `delete_document`, `delete_media`, `delete_form`, and `delete_form_entry` now
166
+ require `confirm: true` in their arguments. The Zod schema rejects the call
167
+ before any HTTP request is made, so an agent cannot delete content (or
168
+ irreversibly destroy form-submission PII) without first acknowledging the
169
+ destructive action. **Behavioral change:** existing callers must add
170
+ `confirm: true` to these tool invocations.
171
+
172
+ ## 0.14.0
173
+
174
+ ### Minor Changes
175
+
176
+ - af555c5: Add MCP form-authoring tools and correct the transport claim (WS-C C1 + C4).
177
+
178
+ Form authoring (C1): the MCP server could previously only READ form submissions. New tools let an agent build and maintain a form end-to-end over MCP: `list_forms`, `get_form`, `create_form`, `update_form`, `delete_form`, `duplicate_form`, `get_form_schema`, `save_form_schema`, plus submissions-inbox management (`set_form_entry_status`, `bulk_set_form_entry_status`, `delete_form_entry`) and `export_form_entries`. CSV export is gated behind `confirm: true` because it is a bulk PII egress, and the underlying client reads the raw `text/csv` body (the success response is not a JSON envelope).
179
+
180
+ Transport + docs truth (C4): the package description advertised "stdio or HTTP" but the server is stdio-only — corrected to "over stdio". Fixed the `repository.url` to the canonical `actuate-media/actuatecms`. Refreshed the README to drop the removed `create_service` / `create_location` / `create_team_member` tools (use `create_in_collection({ template })`) and document the form-authoring and page-section tool families.
181
+
182
+ - 2eac1b5: Add MCP navigation-menu helpers (WS-C C3) so agents can build and wire up site menus.
183
+
184
+ Menus are documents in the `navigations` collection with a recursive link tree on `data.items` (the same shape the admin Nav builder uses). Nine new tree-aware tools wrap the document API:
185
+ - `list_navigations` / `get_navigation` — list menus (`{ id, name, slug, location, itemCount }`) or read one with its full tree.
186
+ - `create_navigation` / `update_navigation` / `delete_navigation` — menu CRUD (slug derived from name; trees validated before save).
187
+ - `add_navigation_item` — add a link by explicit `{ label, url }` or by a `page` reference (`{ collection?, slug? | id? }`), which resolves the page's public URL and label automatically ("link page X into menu Y"); optional `parentId` nests it.
188
+ - `remove_navigation_item` / `reorder_navigation_items` — remove one item (and its sub-tree) or reorder one level by an exact id permutation.
189
+ - `validate_navigation_tree` — validate a stored menu or a candidate `items` array without saving (`{ valid, errors, warnings, stats }`).
190
+
191
+ All writes go through `update_document` semantics, preserving versioning, access control, hooks, and cache invalidation. New items get admin-compatible ids generated so menus authored over MCP stay editable in the UI.
192
+
193
+ - a413616: Add MCP relationship-resolver tools (WS-C C2) so agents stop guessing document IDs.
194
+ - **cms-core (additive, backward-compatible):**
195
+ - `listDocuments` / `GET /collections/:slug` now accept an exact `slug` filter. Because `slug` is `@@unique([collection, slug])`, this is the canonical single-query slug→id lookup.
196
+ - The `GET /collections` discovery endpoint now surfaces `relationTo` (for relationship fields), `hasMany`, and select `options` in each field summary, so an agent can learn where a relationship field points. (Overlaps the WS-B4 schema-surfacing work.)
197
+ - **mcp-server:** three new tools — `get_relationship_fields` (list relationship/media/select fields + their `relationTo`), `resolve_relationship` (exact slug → `{ id, slug, title, status }`), and `list_relationship_targets` (browse candidate documents when the slug is unknown). README updated with a "Resolving relationships" section.
198
+
199
+ ## 0.13.1
200
+
201
+ ### Patch Changes
202
+
203
+ - ae1af8f: Authoring correctness fixes for the AI site-building workflow (roadmap WS-B/E quick wins).
204
+ - **cms-core**: `createDocument` now honors a requested `status` (`DRAFT` or
205
+ `PUBLISHED`) instead of always forcing `DRAFT`. Publishing on create is gated
206
+ to EDITOR/ADMIN, stamps `publishedAt`, and emits the `document.published`
207
+ webhook + sitemap notify. This fixes `POST /page-builder/create?publish=true`
208
+ (and `status: 'PUBLISHED'`), which previously created a draft silently.
209
+ Statuses other than `DRAFT`/`PUBLISHED` on create are rejected with guidance
210
+ to schedule/archive via update.
211
+ - **client**: `listMedia()` now returns the canonical
212
+ `{ docs, total, page, pageSize, totalPages }` (`MediaList`) envelope. The
213
+ server returns the asset array under `items`/`data` (never `docs`), so the
214
+ previous typing made `listMedia().docs` always `undefined`. Adds exported
215
+ `MediaAsset` and `MediaList` types and an optional `init` (signal/cache) arg.
216
+ - **mcp-server**: the `list_media` tool now normalizes the same `items`→`docs`
217
+ shape so its output matches `list_documents`.
218
+ - **create-actuate-cms**: generated `TextBlock`/`ImageTextBlock` now sanitize
219
+ author-supplied HTML with `sanitizeHtml` before `dangerouslySetInnerHTML`
220
+ (was an XSS vector on hostile content).
221
+
222
+ ## 0.13.0
223
+
224
+ ### Minor Changes
225
+
226
+ - a6d6674: Forms PII/encryption, retention, audit, export, and MCP tools (PR8).
227
+
228
+ **cms-core**
229
+ - **Field-level encryption at rest**: submission values for fields flagged `encrypted` (or high-PII fields when `forms.encryptSensitiveFields` is enabled) are now encrypted with the `enc:v1:` AES-256-GCM envelope before persistence and transparently decrypted on every read path. Plaintext/legacy rows pass through unchanged; missing key on read fails closed.
230
+ - **PII retention**: `forms.retentionDays` is now wired into the cleanup cron, hard-deleting form submissions (and cascading their files) older than the window. Opt-in — disabled by default.
231
+ - **Audit logging**: viewing a single entry now logs `form.entry.viewed` and CSV export logs `form.entries.exported`, both with user id and IP, for PII-access traceability.
232
+ - **CSV export endpoint**: `GET /forms/entries/export?formId=…` streams a decrypted CSV of a form's submissions (bounded to 5000 rows) with formula-injection-safe cells. New exported helpers `exportFormEntries`, `encryptSubmissionData`, `decryptSubmissionData`, `fieldShouldEncrypt`.
233
+
234
+ **mcp-server**
235
+ - New `list_form_entries` and `get_form_entry` tools over the decrypted submissions inbox, both requiring an explicit `formId`/`entryId` (no enumeration) and documented as PII-sensitive. `get_form_entry` reads are audited server-side.
236
+
237
+ ## 0.12.0
238
+
239
+ ### Minor Changes
240
+
241
+ - 5fa84d3: SEO Operations Center — a full detect → prioritize → explain → route → fix → audit → generate workflow.
242
+
243
+ **cms-core**
244
+ - Additive `0009_seo` migration (mirrored to `apps/dev`): `SEOAuditRun`, `SEOIssue` (+ severity/status/category enums), `Redirect` extensions (`hits`, `lastHitAt`, `enabled`, `priority`, `preserveQuery`, `source`), `Redirect404Hit`, and `RedirectSuggestion`. All columns are nullable or defaulted, so it is safe to apply to an existing database.
245
+ - Pure, explainable scoring model (`seo/score.ts`): `calculatePageSeoScore` / `calculateSiteSeoScore` with documented weights and breakdowns.
246
+ - Persistent audit engine (`seo/audit-engine.ts`) with cross-run issue lifecycle (open / resolved / ignored / recurring) and a redirect engine (chain/loop detection, safe flatten, hit tracking, 404 logging, gated AI 404 recovery).
247
+ - New endpoints: `POST /seo/audit`, `GET /seo/audits[/:id]`, `GET /seo/issues[/:id]`, issue `resolve`/`ignore`/`reopen`/`fix`, `GET /seo/overview`, redirect CRUD + import/export + chains/loops/flatten/suggest, technical settings (`/seo/sitemap/*`, `/seo/crawl-settings`, `/seo/robots`), and approval-gated `/ai/seo/*` (generate-field, explain-issue, generate-schema, summarize) that degrade gracefully with 501 when `plugin-ai` is unconfigured.
248
+ - `/resolve` now returns Next.js-ready `metadata` plus validated JSON-LD; public `robots.txt` honors the admin-edited body.
249
+
250
+ **cms-admin**
251
+ - Rebuilt `SEO.tsx` into a five-tab Operations Center (Overview / Content / Technical / Redirects / Audit) with `?tab=` deep-link routing, dynamic badge counts, a typed `seo-service.ts` client, reusable token-only primitives and a focus-trapped drawer, editor/fix/copilot drawers, and full loading / empty / error / permission states.
252
+
253
+ **mcp-server**
254
+ - New SEO tools: `seo_overview`, `run_seo_audit`, `list_seo_audit_runs`, `list_seo_issues`, `get_seo_issue`, `set_seo_issue_status`, `list_redirect_rules`, `seo_redirect_overview`, `seo_redirect_chains`.
255
+
256
+ **create-actuate-cms**
257
+ - Scaffolded projects now ship a redirect-resolving `middleware.ts` backed by the database redirect table.
258
+
259
+ ## 0.11.0
260
+
261
+ ### Minor Changes
262
+
263
+ - 91885de: Add a section-aware bridge so AI agents (e.g. in Cursor / VS Code) can build pages section-by-section through MCP, producing real, editable, publishable Page Editor sections rather than disposable markup.
264
+ - **cms-core**: new `@actuate-media/cms-core/page-sections` subpath exporting the flat Page Editor section registry, pure section-array helpers, and a deterministic `validateSectionContent`. This is now the single source of truth shared by the admin editor, the REST API, and MCP. Adds REST routes: `GET /page-sections/types`, `POST /page-sections/validate`, and section CRUD over a page document's `data.sections` (`GET|POST /page-sections/:collection/:id`, `PATCH|DELETE /page-sections/:collection/:id/:sectionId`, `POST /page-sections/:collection/:id/reorder`). Mutations go through the audited `updateDocument` path (versions, hooks, cache invalidation, live-preview push) and reuse collection scope checks. Writes are rejected on collections that don't declare a `sections` JSON field.
265
+ - **mcp-server**: nine new tools — `list_section_types`, `get_section_schema`, `validate_section_content`, `list_page_sections`, `add_page_section`, `update_page_section`, `reorder_page_sections`, `remove_page_section`, `toggle_page_section_visibility`.
266
+ - **cms-admin**: the Page Editor section registry and section helpers now re-export from `@actuate-media/cms-core/page-sections`; per-section publish validation is delegated to the shared `validateSectionContent`. No behavior change — internal refactor for a single source of truth.
267
+
268
+ - 91885de: Add a design-driven Posts template system that mirrors the Pages section editor.
269
+ - **cms-core**: tag section types with a `scope` (`page` | `post`), add post-oriented section types (`article-body`, `quote`, `author-bio`, `related-posts`, `gallery`), `PostHeaderConfig` / `PostTemplate` types, the `getSectionTypesForScope` helper, and new field types (`richText`, `boolean`, `relationship`, `gallery`). Adds `GET/PUT /post-templates/:type` API routes and a `?scope=` filter on `GET /page-sections/types`.
270
+ - **cms-admin**: new `PostSectionEditor`, `PostTemplateEditor` (with header panel), `PostPreview`, post section renderers, and a `post-editor-service` data layer. The Add Section gallery now filters by scope, and post type cards expose an "Edit template" action.
271
+ - **mcp-server**: scope-aware `list_section_types`, plus `get_post_template` and `save_post_template` tools so an external AI agent can read and build a post type's template.
272
+
273
+ ## 0.10.0
274
+
275
+ ### Minor Changes
276
+
277
+ - a0f6eba: Rich `@`-mention picker for comments (last Phase 7 carry-over).
278
+
279
+ **`cms-core`**: New `GET /api/cms/users/search?q=<query>` endpoint —
280
+ authenticated (any role), case-insensitive `contains` matching against
281
+ `name` + `email`, capped at 10 results, returns only `{ id, name,
282
+ email }` (no role / hash / audit metadata). Empty / whitespace
283
+ queries return an empty array so the endpoint can't be used as a
284
+ directory-enumeration tool.
285
+
286
+ **`cms-admin`**: New `<MentionableTextarea>` component plus pure
287
+ helpers in `lib/mention-picker.ts` (`detectMentionContext`,
288
+ `insertMentionToken`, `searchMentionUsers`). Wired into the three
289
+ comment composer surfaces in `CommentSidePanel`: the new-thread
290
+ textarea, the reply input, and the in-place edit textarea. The
291
+ picker is keyboard-driven (Arrow Up / Down, Enter / Tab to commit,
292
+ Escape to close) with a Radix-style listbox + ARIA roles, and
293
+ gracefully handles slow responses (drops stale requests when typing
294
+ quickly).
295
+
296
+ **`mcp-server`**: New `search_users(query)` tool exposes the same
297
+ lookup so AI agents can resolve teammates by name fragment before
298
+ drafting a comment. Wire format is unchanged — the picker writes
299
+ `@<userId>` tokens that the existing `extractMentionedUserIds`
300
+ parser already understands, so no notification schema migration is
301
+ required.
302
+
303
+ No breaking changes.
304
+
305
+ ## 0.9.0
306
+
307
+ ### Minor Changes
308
+
309
+ - 21db54b: Phase 7 PR F — MCP-parity audit stylistic cleanup.
310
+
311
+ This is the final follow-up to `docs/mcp-parity-audit.md`. After this
312
+ PR the audit is fully retired — P0, P1, and stylistic notes are all
313
+ addressed.
314
+
315
+ ### `@actuate-media/cms-core`
316
+
317
+ Adds `GET /api/cms/collections`: a lightweight discovery endpoint
318
+ sourced directly from `getActuateConfig()`. Returns
319
+ `{ data: CollectionMeta[] }` with one entry per configured collection
320
+ (`slug`, `labels`, `type`, `urlPrefix`, `hidden`, `fieldCount`,
321
+ `fields[{name,type,required}]`). Unauthenticated by design — the
322
+ response only exposes schema-shape metadata that is already public via
323
+ `/openapi.json` — and the contract is intentionally tight (only
324
+ `name` / `type` / `required` per field, no validators, hooks, or
325
+ access closures leak).
326
+
327
+ ### `@actuate-media/mcp-server`
328
+
329
+ Three audit-mandated stylistic cleanups:
330
+ 1. **AI-create-X collapse (6 tools → 3).** Removed `create_service`,
331
+ `create_location`, `create_team_member`. Their behaviour is folded
332
+ into `create_in_collection` via an optional `template` arg
333
+ (`'service' | 'location' | 'team_member' | 'generic'`) that
334
+ selects a built-in prompt-enrichment recipe plus a default
335
+ collection slug. Kept `create_blog_post` and `create_case_study`
336
+ as type-specific tools because their field-sets are distinctive
337
+ enough to be standalone agent intents.
338
+
339
+ **Breaking change** (acknowledged — pre-1.0, audit-recommended):
340
+ the three removed tool names will return "tool not found" on next
341
+ release. Migration is mechanical:
342
+ - `create_service({ category, ... })`
343
+ → `create_in_collection({ template: 'service', category, ... })`
344
+ - `create_location({ cityState, ... })`
345
+ → `create_in_collection({ template: 'location', cityState, ... })`
346
+ - `create_team_member({ name, role, ... })`
347
+ → `create_in_collection({ template: 'team_member', name, role, ... })`
348
+
349
+ 2. **`audit_document` split.** Replaced with `audit_document_by_id`
350
+ (requires `collection` + `id`) and `audit_inline_content` (requires
351
+ `title` + `body`). Both forward to the same `/ai/audit-document`
352
+ endpoint; the split exists so agents pick the right mode at the
353
+ tool boundary instead of constructing a half-filled blob and
354
+ discovering the "must provide either (collection,id) or body" rule
355
+ via a server-side error.
356
+
357
+ **Breaking change** (audit-recommended): `audit_document` is
358
+ removed. Migration: pick the explicit tool that matches the
359
+ intent — `audit_document_by_id` for stored docs,
360
+ `audit_inline_content` for ad-hoc text.
361
+
362
+ 3. **`list_collections` reliability.** Now prefers the new
363
+ `/api/cms/collections` endpoint as the primary source so the tool
364
+ stays accurate when the OpenAPI generator drifts. Falls back to
365
+ OpenAPI only on a real HTTP 404 (older CMS server) — the decision
366
+ uses `ActuateClientError.status` (newly exported from the client),
367
+ not a regex against the error message, so an unrelated server
368
+ error that happens to contain "not found" in its body no longer
369
+ triggers the fallback. Non-404 errors propagate.
370
+
371
+ **Return-shape change** (consistent contract): `data` is now
372
+ ALWAYS a `CollectionSummary[]` array regardless of which path
373
+ responded. The OpenAPI fallback path normalises into the same
374
+ array shape that the new `/collections` endpoint emits, so
375
+ callers no longer need to branch on `source` to know whether they
376
+ got an array or an object map. The returned envelope is
377
+ `{ data: CollectionSummary[], source: 'collections' | 'openapi' }`.
378
+
379
+ **Newly exported:** `ActuateClientError` (subclass of `Error`
380
+ carrying `status: number`). Useful for any consumer that needs
381
+ to branch on the HTTP status from a `request()` failure.
382
+
383
+ ### Tests
384
+ - mcp-server: 113 tests total (was 97 in PR E). 16 new — template
385
+ routing for each of service / location / team_member, audit split
386
+ validation, list_collections primary + fallback + 500-propagation.
387
+ - cms-core: 4 new tests for the discovery endpoint covering field
388
+ summaries, `hidden` flag, missing-config 500, and the
389
+ no-leak-of-validators-or-hooks contract.
390
+
391
+ ## 0.8.0
392
+
393
+ ### Minor Changes
394
+
395
+ - e0ac7f1: Add 4 comment + page-builder accessibility MCP tools (Phase 7 MCP-parity PR D). Combined with PRs A (10 media/folder), B (6 versions/scheduling/workflow), and C (5 SEO), this closes the remaining P0 gaps from [`docs/mcp-parity-audit.md`](https://github.com/actuate-media/actuatecms/blob/main/docs/mcp-parity-audit.md) and brings the MCP server to **100% P0 coverage**.
396
+
397
+ **Comments (2):**
398
+ - `list_comments` — returns the comment thread for a document. Default scope is unresolved + non-deleted; `includeResolved: true` widens to resolved threads; `includeDeleted: true` is admin-only and surfaces soft-deleted moderation history (non-admin keys get HTTP 403).
399
+ - `create_comment` — create a top-level comment, a thread reply (`parentId`), or a text-pinned comment (`anchor: { from, to }`). The wrapper rejects empty `body` strings at the Zod layer (server also rejects, but failing fast saves a round-trip). The server emits a `comment_created` audit event and fires a best-effort notification dispatch for any mentioned users.
400
+
401
+ **Page-builder accessibility (2):**
402
+ - `audit_page_a11y` — deterministic accessibility audit over a page-builder tree. Detects missing alt text, heading-level skips, missing form labels, missing ARIA attributes, and undersized touch targets. Each issue carries `autoFixable: boolean` so the caller can decide between automated cleanup vs human review.
403
+ - `fix_page_a11y` — auto-fix every `autoFixable: true` issue and return the mutated tree plus a report (`fixedCount` / `remainingCount` plus the unfixable remainder for human follow-up). Emits a `settings_changed` audit event.
404
+
405
+ **Schema design notes:**
406
+ - Both `list_comments` and `create_comment` use `id` (not `documentId`) for the document ID — matches the PR B convention and the pre-existing `audit_document` / `get_document` / `update_document` family. The collection slug is intentionally absent because comments attach to documents by ID alone.
407
+ - `create_comment.anchor` and `create_comment.parentId` are both `.nullable().optional()`. Publishing `["object", "null"]` and `["string", "null"]` in the JSON Schema preserves the server semantics where `null` is meaningful ("thread-level comment" / "no anchor"), not an MCP-client artefact.
408
+ - A11y tools take `tree: z.record(z.unknown())` — the page-builder schema is large and evolving so we accept any object and let the server enforce the top-level `tree.type === 'page'` check. Strings / numbers / arrays still fail at the Zod layer.
409
+
410
+ **Tests:** mcp-server suite 56 → 70 (+14). Covers per-method URL / method / body shape, boolean → string query encoding for `listComments`, URL encoding for awkward IDs, 403 propagation for read-only keys hitting `create_comment`, 400 propagation for malformed page trees, anchor / parentId nullable JSON Schema emission, and empty-body rejection at the wrapper layer.
411
+
412
+ This PR closes the audit's last P0 acceptance criterion. PR E (~14 P1 mop-up tools) and PR F (stylistic cleanup — collapse the `ai_create_*` family, fix the `audit_document` schema drift, OpenAPI fallback) remain as demand-driven cherry-picks.
413
+
414
+ - 942ad41: Add 14 MCP tools — P1 mop-up from the MCP-parity audit. Phase 7, PR E.
415
+
416
+ Tools shipped:
417
+
418
+ Comment thread management (4):
419
+ - `edit_comment` — `PATCH /comments/:id`
420
+ - `resolve_comment` — `POST /comments/:id/resolve`
421
+ - `reopen_comment` — `POST /comments/:id/reopen`
422
+ - `delete_comment` — `DELETE /comments/:id`
423
+
424
+ Redirects (3) — admin-only on the server, open-redirect defence preserved:
425
+ - `list_redirects` — `GET /redirects`
426
+ - `create_redirect` — `POST /redirects` (statusCode constrained to 301/302/307/308 via schema enum)
427
+ - `delete_redirect` — `DELETE /redirects/:id`
428
+
429
+ Page templates + saved sections (3) — admin-only discovery for the page builder:
430
+ - `list_page_templates` — `GET /page-templates` (optional category + collection filters)
431
+ - `get_page_template` — `GET /page-templates/:id`
432
+ - `list_saved_sections` — `GET /saved-sections`
433
+
434
+ SEO aggregate (2):
435
+ - `seo_summary` — cheap dashboard aggregate (`GET /seo/summary`)
436
+ - `seo_scan_run` — expensive site-wide scan (`POST /seo/scan`); wrapper enforces a `confirm: true` literal per the audit's gate-behind-confirmation recommendation
437
+
438
+ Forms + health (2):
439
+ - `list_form_submissions` — PII-sensitive; requires explicit `formId` arg, no enumeration (`GET /forms/:formId/submissions`)
440
+ - `health_check` — `GET /health` (unauthenticated on the server; safe first-call probe)
441
+
442
+ Notes:
443
+ - Comment-thread tools follow the `id` naming convention established in
444
+ PR B / PR D for resource-scoped tools.
445
+ - `list_form_submissions` uses `formId` (not `id`) because the audit
446
+ explicitly called out "gate behind explicit form-id arg" — the more
447
+ specific name makes the PII gate self-documenting in agent prompts.
448
+ - `seo_scan_run` rejects calls without `confirm: true` before issuing
449
+ the HTTP request. Defence-in-depth: Zod `.literal(true)` rejects at
450
+ the schema layer and the handler re-asserts so a schema regression
451
+ still fails closed.
452
+ - All 14 tools forward server errors verbatim (403 admin gate, 400
453
+ open-redirect defence, 403 author-only delete/edit) so agents see the
454
+ same affordances the admin UI does.
455
+ - 24 new tests (15 client + 9 tool-level wrappers, including
456
+ confirmation-gate, PII-naming, and enum-validation regressions).
457
+ Brings the mcp-server test suite to 94 tests total.
458
+
459
+ This closes out the P1 list from `docs/mcp-parity-audit.md`. After
460
+ PR E, every admin action the audit flagged as user-visible (P0 + P1) is
461
+ reachable via MCP. Only PR F (stylistic cleanup — collapsing
462
+ `ai-create-X`, fixing `audit_document` schema, OpenAPI fallback)
463
+ remains as demand-driven follow-up.
464
+
465
+ ## 0.7.0
466
+
467
+ ### Minor Changes
468
+
469
+ - 9517da2: Add 6 versions + scheduling + workflow MCP tools (Phase 7 MCP-parity PR B). Combined with PRs A (10 media/folder tools) and C (5 SEO tools), this retires **21 of the 24 P0 gaps** identified in [`docs/mcp-parity-audit.md`](https://github.com/actuate-media/actuatecms/blob/main/docs/mcp-parity-audit.md). The remaining 3 P0 items are comments + page-builder accessibility, queued as PR D.
470
+
471
+ **Version history (2):**
472
+ - `list_versions` — immutable change history of a document, newest first. Each version carries the full `data` payload at write time plus the user who made the change. Paged (default 20 / max 50).
473
+ - `restore_version` — roll a document back to a prior version. Goes through the normal `updateDocument` path: hooks fire, a NEW version row is created (history is never rewritten), and an audit log entry is emitted.
474
+
475
+ **Scheduling (2):**
476
+ - `schedule_publish` — schedule a document to publish (`scheduledAt`) and/or unpublish (`scheduledUnpublishAt`) at a future ISO 8601 timestamp. The server requires at least one of the two, both must be in the future, and `scheduledUnpublishAt` must be after `scheduledAt` when both are set. Publishing forces `status` → `SCHEDULED` so the `/cron/publish` cron picks it up; the cron does the actual status flip (decoupled so a missed tick just delays publish — it can never double-publish or skip a doc).
477
+ - `unschedule_publish` — cancel pending publish + unpublish. If the document was in `SCHEDULED` (not yet published) it drops back to `DRAFT`. Already-published docs stay live.
478
+
479
+ **Editorial workflow (2):**
480
+ - `get_workflow_state` — returns `{ stage, transitions, reviewerId, reviewNote }`. `transitions` is the list of next stages the _calling API key holder_ is allowed to move to, already role-filtered server-side. Workflow: `DRAFT → IN_REVIEW → APPROVED → PUBLISHED → ARCHIVED`.
481
+ - `transition_workflow` — move a document to a new stage. Server validates both that the from→to edge is legal AND that the caller's role permits it; either failure returns HTTP 400 with the reason in the error body. The `stage` arg is a Zod enum, so unknown stages are rejected at the wrapper layer before the HTTP call (clear schema error, no wasted round-trip).
482
+
483
+ **Tests:** mcp-server suite 40 → 53 (+13). Coverage includes per-method URL / method / body shape, optional-field independence on `schedule_publish`, enum validation on `transition_workflow.stage`, error propagation for both 400 paths (illegal transitions, past timestamps), and URL encoding of awkward collection slugs + document IDs.
484
+
485
+ ## 0.6.0
486
+
487
+ ### Minor Changes
488
+
489
+ - a142310: Add 10 media + folder MCP tools (Phase 7 MCP-parity PR A). Combined with PR C (5 SEO tools, shipped in PR #145), this retires 15 of the 24 P0 gaps identified in [`docs/mcp-parity-audit.md`](https://github.com/actuate-media/actuatecms/blob/main/docs/mcp-parity-audit.md).
490
+
491
+ **Media write tools (5):**
492
+ - `upload_media` — uploads a file to the media library. Bytes pass through MCP as base64 (JSON-RPC has no binary type), the tool wrapper decodes them, and the client emits a real `multipart/form-data` request. The server enforces a 50 MB hard cap, validates magic bytes against the declared `mimeType`, and sanitises SVGs.
493
+ - `update_media` — set alt text (accessibility), title, filename, or focal points (smart-crop hints in 0..1 normalised coords).
494
+ - `delete_media` — removes the DB row AND deletes the underlying blob storage object (best-effort).
495
+ - `optimize_media` — recompress an existing image to WebP. Returns the updated row plus an `optimization` summary. Already-WebP files short-circuit.
496
+ - `move_media_to_folder` — `folderId: string | null`; the server validates the target folder is in the `media` scope.
497
+
498
+ **Folder tools (5):**
499
+ - `list_folders` — returns a nested tree (`children[]`) for a given scope. Scopes today: `"media"`, `"documents"`, `"collections"`. The `scope` arg is required.
500
+ - `create_folder` — `{ name, scope, parentId? }`. Parents must already exist in the same scope.
501
+ - `update_folder` — rename / reparent / reorder; pass any subset of `{ name, parentId, position }`. `parentId: null` promotes a folder to root. The server prevents self-parenting and cross-scope reparenting.
502
+ - `delete_folder` — returns HTTP 409 if the folder still contains documents, media, or sub-folders. The caller must move or delete the contents first.
503
+ - `move_document_to_folder` — same shape as `move_media_to_folder`, scoped to documents/collections folders.
504
+
505
+ **Tooling improvements:**
506
+ - Added a `requestMultipart()` helper to `ActuateClient` for endpoints that need `multipart/form-data`. It deliberately omits the `Content-Type` header so the fetch runtime can append the multipart boundary (overriding it breaks server-side parsing).
507
+ - Extended the Zod → JSON Schema converter to handle `ZodNullable`. Nullable fields now publish `type: ["string", "null"]` (etc.), so MCP-aware clients accept explicit `null` from agents. The `move_*_to_folder` and `update_folder` tools rely on this.
508
+
509
+ 37 new unit tests (mcp-server suite: 21 → 38). Includes regression tests for: multipart body shape, missing `Content-Type` override, base64 with data-URL prefix + whitespace, empty-bytes rejection, null `folderId` round-trip on both move tools, and URL encoding of awkward IDs.
510
+
511
+ ## 0.5.0
512
+
513
+ ### Minor Changes
514
+
515
+ - 8d105c3: Add five per-document SEO MCP tools (Phase 7 MCP-parity PR C):
516
+ - `seo_analyze_document` — runs the deterministic SEO content analysis (title, meta, headings, focus keyphrase, link counts, score) against a stored document.
517
+ - `seo_readability_score` — Flesch-Kincaid reading-ease + grade-level for a document body.
518
+ - `seo_internal_link_targets` — suggests up to 10 published documents to link TO, rooted in a specific stored document (title-keyword-based). Distinct from the existing `suggest_internal_links` tool, which is content-based and freeform.
519
+ - `seo_meta_preview` — resolved meta-tag preview (title, description, canonical, OG, Twitter, robots) for a document, with per-page overrides and site-level defaults applied.
520
+ - `seo_schema_graph` — Schema.org JSON-LD graph (`@context` + `@graph` with WebSite / WebPage / Article nodes) for a document.
521
+
522
+ All five are read-only deterministic wrappers around the existing `GET /api/cms/seo/<scope>/:documentId` endpoints — no LLM calls, no DB writes, cheap enough for an agent to invoke as part of a publish-readiness pipeline. Each requires only a `documentId` arg.
523
+
524
+ This is the first follow-up PR from the [MCP-parity audit](https://github.com/actuate-media/actuatecms/blob/main/docs/mcp-parity-audit.md) (PR #144), retiring 5 of the 24 P0 gaps identified there.
525
+
526
+ ## 0.4.0
527
+
528
+ ### Minor Changes
529
+
530
+ - 0ac9024: Inline AI co-author HTTP endpoint + MCP tool (Phase 2).
531
+
532
+ Adds `POST /api/cms/ai/coauthor` with four sub-actions (`rewrite`,
533
+ `expand`, `compress`, `proofread`) that wrap the plugin-ai writing
534
+ primitives behind a single endpoint. Rate-limited under the same
535
+ bucket as content generation (20 calls/hour/user). Missing provider
536
+ configuration surfaces as HTTP 501 instead of 500.
537
+
538
+ Exposed to AI agents via the MCP tool `coauthor_text` with the same
539
+ input shape, so the admin UI and AI agents can drive identical
540
+ text-transformation workflows.
541
+
542
+ Tests: 5 endpoint tests covering each action + validation errors.
543
+
544
+ - 313a23f: Add Slice F — AI quality pass.
545
+
546
+ `plugin-ai` exposes a new `auditDocument()` composer that combines SEO,
547
+ readability (Flesch-Kincaid / Gunning Fog / SMOG averaged + remapped to a
548
+ 0-100 score), accessibility, and freshness scoring into a single
549
+ composite grade with prioritized actionable issues. The composer is
550
+ deterministic — it never calls an LLM — so it's cheap enough to invoke
551
+ on every save or debounced keystroke.
552
+
553
+ `cms-core` adds two HTTP endpoints:
554
+ - `POST /ai/audit-document` — runs the composite audit against a stored
555
+ document (`{ collection, id }`) or ad-hoc content. Honors field-level
556
+ access control when loading from the DB.
557
+ - `POST /ai/suggest-internal-links` — scans published documents for
558
+ phrases the supplied content could link to, returning ranked
559
+ anchor-text + target-URL suggestions. Optionally scopes to a single
560
+ collection.
561
+
562
+ `mcp-server` exposes both endpoints as MCP tools (`audit_document` and
563
+ `suggest_internal_links`) so AI agents can grade drafts and propose
564
+ internal links before publish.
565
+
566
+ Docs: `docs/ai-quality-audit.md`.
567
+
568
+ ## 0.3.0
569
+
570
+ ### Minor Changes
571
+
572
+ - e6a18b5: Phase 2 Slice C — AI Content Authoring for any collection.
573
+ - **plugin-ai**: new `generateDocumentContent()` helper that maps a prompt onto a collection's field schema and returns structured field data ready to persist. Supports tone, target audience, brand voice profile, and existing-content context. Coerces LLM output to declared field types (numbers, booleans, dates, select options, array items) and drops fields the schema doesn't declare.
574
+ - **cms-core**: new `POST /collections/:slug/ai-create` endpoint — the non-page counterpart to `/page-builder/create`. Generates a complete document for any collection, persists as draft, optionally publishes via a follow-up update when the caller has admin scope. Refuses page-builder collections (use `/page-builder/create` instead). Hard caps prompt at 4000 chars and shares the per-user 20/hour AI rate-limit bucket. Returns a `warning` field when create succeeds but publish is denied.
575
+ - **mcp-server**: six new tools — `create_in_collection` (generic) plus per-type sugar tools `create_blog_post`, `create_case_study` (with `clientName` / `industry` hints), `create_service` (`category` hint), `create_location` (`cityState` hint), and `create_team_member` (`name` / `role` hints). Each per-type tool targets the conventional collection slug shipped by the cms-core collection presets, with an explicit `collection` override for sites using custom slugs.
576
+ - 19 new tests (10 unit tests in plugin-ai for the content generator, 9 integration tests in cms-core for the API endpoint covering auth, validation, persistence, publish flow, and the page-builder refusal path).
577
+ - Docs: updated `docs/api-reference.md` with the new endpoint, `docs/mcp.md` and `packages/mcp-server/README.md` with the new tools and example agent prompts.
578
+
579
+ ## 0.2.0
580
+
581
+ ### Minor Changes
582
+
583
+ - e87471d: Phase 1: AI access. API key authentication (act*sk*\* bearer tokens) with scope-based authorization across collections, globals, media, and the page builder. New /api-keys admin endpoints + UI for creating, listing, and revoking keys with password reauth. CSRF is skipped for API-key requests. New /page-builder/create one-shot endpoint that generates an AI page and persists it as a document in a single call. New @actuate-media/mcp-server package exposes the CMS to AI agents (Claude, Cursor, ChatGPT, etc.) via the Model Context Protocol — full CRUD, globals, media, page generation, and one-shot create tools. Adds 44 new tests covering the API key auth path, scope enforcement, CSRF skip, and MCP client/tools.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Actuate Media
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.