@rimelight/cms 0.0.8 → 0.0.10
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.mts +1832 -16
- package/dist/index.mjs +1534 -218
- package/dist/schema/index.d.mts +257 -0
- package/dist/schema/index.mjs +18 -2
- package/dist/schema/sqlite.d.mts +232 -0
- package/dist/schema/sqlite.mjs +15 -1
- package/package.json +29 -16
- package/src/admin/api/media-file.ts +0 -1
- package/src/admin/api/media.ts +0 -2
- package/src/admin/layouts/CMSDashboardLayout.astro +7 -1
- package/src/admin/layouts/DefaultParentLayout.astro +0 -1
- package/src/admin/pages/pages-edit.astro +4 -1
- package/src/admin/pages/pages-index.astro +262 -101
- package/src/admin/pages/pages-preview.astro +5 -2
- package/src/admin/pages/templates-index.astro +224 -54
- package/src/admin/pages/users.astro +96 -50
- package/src/admin/pages/versions.astro +49 -37
- package/src/astro/blocks/DialogueBlock.astro +25 -0
- package/src/astro/blocks/LivePreviewBlock.astro +33 -0
- package/src/astro/blocks/SceneBlock.astro +39 -0
- package/src/astro/blocks/ScriptBlock.astro +77 -0
- package/src/astro/blocks/TableBlock.astro +30 -37
- package/src/astro/blocks/index.ts +9 -1
- package/src/env.d.ts +7 -5
- package/src/loader/live.ts +1 -2
- package/src/mcp/index.ts +1290 -32
- package/src/schema/content_types.ts +59 -0
- package/src/schema/index.ts +1 -0
- package/src/schema/sqlite.ts +35 -0
- package/src/services/search-indexer.ts +20 -9
- package/src/services/site-settings.ts +18 -14
- package/src/services/template-seeder.ts +249 -0
- package/src/storage/index.ts +0 -1
- package/LICENSE +0 -21
package/src/mcp/index.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import { and, desc, eq, isNotNull, isNull } from "drizzle-orm"
|
|
1
|
+
import { and, desc, eq, isNotNull, isNull, like, or, sql } from "drizzle-orm"
|
|
2
2
|
import { pages } from "../schema/pages.ts"
|
|
3
3
|
import { pageDrafts } from "../schema/page_drafts.ts"
|
|
4
|
+
import { pageVersions } from "../schema/page_versions.ts"
|
|
5
|
+
import { pageTemplates } from "../schema/page_templates.ts"
|
|
6
|
+
import { contentTypes } from "../schema/content_types.ts"
|
|
7
|
+
import { bylines } from "../schema/bylines.ts"
|
|
8
|
+
import { taxonomyTerms, pageTaxonomyTerms } from "../schema/taxonomies.ts"
|
|
9
|
+
import { pageVersionApprovals } from "../schema/page_version_approvals.ts"
|
|
10
|
+
import { pageVersionComments } from "../schema/page_version_comments.ts"
|
|
4
11
|
import { getSiteSettings, updateSiteSettings } from "../services/site-settings.ts"
|
|
12
|
+
import { getTaxonomyTerms } from "../services/taxonomies.ts"
|
|
13
|
+
import { indexPageForSearch } from "../services/search-indexer.ts"
|
|
5
14
|
import type { PageType } from "../core/types/pages.ts"
|
|
6
15
|
import type { BaseBlock } from "../core/types/blocks.ts"
|
|
7
16
|
|
|
8
17
|
export const CMS_MCP_TOOLS = [
|
|
18
|
+
// ─── 1. Pages & Drafts ──────────────────────────────────────────────────────
|
|
9
19
|
{
|
|
10
20
|
name: "cms_list_pages",
|
|
11
21
|
description: "List CMS pages with optional filters for type, draft status, and pagination.",
|
|
@@ -14,17 +24,18 @@ export const CMS_MCP_TOOLS = [
|
|
|
14
24
|
properties: {
|
|
15
25
|
type: {
|
|
16
26
|
type: "string",
|
|
17
|
-
description: "Filter by page type (e.g. 'blog', '
|
|
27
|
+
description: "Filter by page type (e.g. 'blog', 'doc', 'legal', 'wiki')"
|
|
18
28
|
},
|
|
19
29
|
includeDrafts: { type: "boolean", description: "Whether to include unpublished drafts" },
|
|
20
|
-
limit: { type: "number", description: "Maximum number of pages to return (default 20)" }
|
|
30
|
+
limit: { type: "number", description: "Maximum number of pages to return (default 20)" },
|
|
31
|
+
offset: { type: "number", description: "Offset for pagination (default 0)" }
|
|
21
32
|
}
|
|
22
33
|
}
|
|
23
34
|
},
|
|
24
35
|
{
|
|
25
36
|
name: "cms_get_page",
|
|
26
37
|
description:
|
|
27
|
-
"Get full details, block tree, and
|
|
38
|
+
"Get full details, block tree, properties, bylines, and active draft for a page by slug or UUID.",
|
|
28
39
|
inputSchema: {
|
|
29
40
|
type: "object",
|
|
30
41
|
properties: {
|
|
@@ -33,20 +44,473 @@ export const CMS_MCP_TOOLS = [
|
|
|
33
44
|
required: ["slugOrId"]
|
|
34
45
|
}
|
|
35
46
|
},
|
|
47
|
+
{
|
|
48
|
+
name: "cms_create_page",
|
|
49
|
+
description: "Create a new CMS page and its initial draft blocks.",
|
|
50
|
+
inputSchema: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
slug: { type: "string", description: "Unique URL slug for the page" },
|
|
54
|
+
type: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "Page type (e.g. 'blog', 'doc', 'legal', 'wiki', 'character')"
|
|
57
|
+
},
|
|
58
|
+
title: { type: "object", description: "Localized title object, e.g. { en: 'Page Title' }" },
|
|
59
|
+
description: {
|
|
60
|
+
type: "object",
|
|
61
|
+
description: "Localized description object, e.g. { en: 'Page description' }"
|
|
62
|
+
},
|
|
63
|
+
templateId: {
|
|
64
|
+
type: "string",
|
|
65
|
+
description: "Optional UUID of the template to base the page on"
|
|
66
|
+
},
|
|
67
|
+
tags: { type: "array", description: "Array of localized tags or strings" },
|
|
68
|
+
authorIds: { type: "array", description: "Array of author user IDs" },
|
|
69
|
+
bylines: {
|
|
70
|
+
type: "array",
|
|
71
|
+
description: "Array of structured byline credits [{ bylineId, role, customCredit }]"
|
|
72
|
+
},
|
|
73
|
+
blocks: { type: "array", description: "Initial structured CMS blocks array" },
|
|
74
|
+
properties: { type: "object", description: "Content type specific custom properties" }
|
|
75
|
+
},
|
|
76
|
+
required: ["slug", "type", "title"]
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "cms_update_page",
|
|
81
|
+
description: "Update an existing CMS page's metadata, properties, bylines, or content blocks.",
|
|
82
|
+
inputSchema: {
|
|
83
|
+
type: "object",
|
|
84
|
+
properties: {
|
|
85
|
+
id: { type: "string", description: "Page UUID" },
|
|
86
|
+
slug: { type: "string", description: "New unique slug" },
|
|
87
|
+
title: { type: "object", description: "Localized title object" },
|
|
88
|
+
description: { type: "object", description: "Localized description object" },
|
|
89
|
+
tags: { type: "array", description: "Array of localized tags" },
|
|
90
|
+
bylines: { type: "array", description: "Array of structured byline credits" },
|
|
91
|
+
blocks: { type: "array", description: "Updated structured CMS blocks array" },
|
|
92
|
+
properties: { type: "object", description: "Updated custom properties object" }
|
|
93
|
+
},
|
|
94
|
+
required: ["id"]
|
|
95
|
+
}
|
|
96
|
+
},
|
|
36
97
|
{
|
|
37
98
|
name: "cms_create_draft",
|
|
38
|
-
description: "Create or update
|
|
99
|
+
description: "Create or update an in-progress draft version for a CMS page without publishing.",
|
|
39
100
|
inputSchema: {
|
|
40
101
|
type: "object",
|
|
41
102
|
properties: {
|
|
42
103
|
pageId: { type: "string", description: "Page UUID" },
|
|
43
|
-
userId: { type: "string", description: "User ID creating the draft" },
|
|
104
|
+
userId: { type: "string", description: "User or Agent ID creating the draft" },
|
|
44
105
|
blocks: { type: "array", description: "Array of structured CMS blocks" },
|
|
45
106
|
properties: { type: "object", description: "Page properties object" }
|
|
46
107
|
},
|
|
47
108
|
required: ["pageId", "userId", "blocks"]
|
|
48
109
|
}
|
|
49
110
|
},
|
|
111
|
+
{
|
|
112
|
+
name: "cms_publish_page",
|
|
113
|
+
description: "Publish a page draft, creating a new immutable version snapshot.",
|
|
114
|
+
inputSchema: {
|
|
115
|
+
type: "object",
|
|
116
|
+
properties: {
|
|
117
|
+
pageId: { type: "string", description: "Page UUID" },
|
|
118
|
+
userId: { type: "string", description: "User or Agent ID publishing the page" },
|
|
119
|
+
changeSummary: { type: "string", description: "Summary of changes in this version" }
|
|
120
|
+
},
|
|
121
|
+
required: ["pageId", "userId"]
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: "cms_unpublish_page",
|
|
126
|
+
description: "Unpublish a live page, reverting it to draft status.",
|
|
127
|
+
inputSchema: {
|
|
128
|
+
type: "object",
|
|
129
|
+
properties: {
|
|
130
|
+
pageId: { type: "string", description: "Page UUID to unpublish" }
|
|
131
|
+
},
|
|
132
|
+
required: ["pageId"]
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "cms_delete_page",
|
|
137
|
+
description: "Soft delete a CMS page by setting deleted_at.",
|
|
138
|
+
inputSchema: {
|
|
139
|
+
type: "object",
|
|
140
|
+
properties: {
|
|
141
|
+
pageId: { type: "string", description: "Page UUID to delete" }
|
|
142
|
+
},
|
|
143
|
+
required: ["pageId"]
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
// ─── 2. Page Versions & Approvals ───────────────────────────────────────────
|
|
148
|
+
{
|
|
149
|
+
name: "cms_list_page_versions",
|
|
150
|
+
description: "List version history snapshots for a page.",
|
|
151
|
+
inputSchema: {
|
|
152
|
+
type: "object",
|
|
153
|
+
properties: {
|
|
154
|
+
pageId: { type: "string", description: "Page UUID" },
|
|
155
|
+
limit: { type: "number", description: "Max versions to return (default 20)" }
|
|
156
|
+
},
|
|
157
|
+
required: ["pageId"]
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: "cms_get_page_version",
|
|
162
|
+
description: "Get full snapshot content and block tree for a specific version.",
|
|
163
|
+
inputSchema: {
|
|
164
|
+
type: "object",
|
|
165
|
+
properties: {
|
|
166
|
+
versionId: { type: "string", description: "Version UUID" }
|
|
167
|
+
},
|
|
168
|
+
required: ["versionId"]
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "cms_rollback_page_version",
|
|
173
|
+
description: "Rollback a page's working draft or live content to a past version snapshot.",
|
|
174
|
+
inputSchema: {
|
|
175
|
+
type: "object",
|
|
176
|
+
properties: {
|
|
177
|
+
pageId: { type: "string", description: "Page UUID" },
|
|
178
|
+
versionId: { type: "string", description: "Target Version UUID to rollback to" },
|
|
179
|
+
userId: { type: "string", description: "User or Agent ID performing rollback" }
|
|
180
|
+
},
|
|
181
|
+
required: ["pageId", "versionId", "userId"]
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: "cms_approve_page_version",
|
|
186
|
+
description: "Record an editorial review approval for a version.",
|
|
187
|
+
inputSchema: {
|
|
188
|
+
type: "object",
|
|
189
|
+
properties: {
|
|
190
|
+
versionId: { type: "string", description: "Version UUID" },
|
|
191
|
+
userId: { type: "string", description: "Approver user or agent ID" },
|
|
192
|
+
userRole: { type: "string", description: "Approver role, e.g. 'Editor', 'Admin'" }
|
|
193
|
+
},
|
|
194
|
+
required: ["versionId", "userId", "userRole"]
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "cms_add_version_comment",
|
|
199
|
+
description: "Add an editorial or block-level comment to a version.",
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
versionId: { type: "string", description: "Version UUID" },
|
|
204
|
+
userId: { type: "string", description: "Author user or agent ID" },
|
|
205
|
+
userRole: { type: "string", description: "Author role" },
|
|
206
|
+
content: { type: "string", description: "Comment body text" },
|
|
207
|
+
blockId: { type: "string", description: "Optional specific block ID target" }
|
|
208
|
+
},
|
|
209
|
+
required: ["versionId", "userId", "userRole", "content"]
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
// ─── 3. Templates ───────────────────────────────────────────────────────────
|
|
214
|
+
{
|
|
215
|
+
name: "cms_list_templates",
|
|
216
|
+
description: "List all page templates with starter blocks and property schemas.",
|
|
217
|
+
inputSchema: {
|
|
218
|
+
type: "object",
|
|
219
|
+
properties: {
|
|
220
|
+
pageType: { type: "string", description: "Optional filter by page type" }
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: "cms_get_template",
|
|
226
|
+
description: "Get template definition by slug or UUID.",
|
|
227
|
+
inputSchema: {
|
|
228
|
+
type: "object",
|
|
229
|
+
properties: {
|
|
230
|
+
slugOrId: { type: "string", description: "Template slug or UUID" }
|
|
231
|
+
},
|
|
232
|
+
required: ["slugOrId"]
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
name: "cms_create_template",
|
|
237
|
+
description: "Create a new page template with default property groups and starter blocks.",
|
|
238
|
+
inputSchema: {
|
|
239
|
+
type: "object",
|
|
240
|
+
properties: {
|
|
241
|
+
slug: { type: "string", description: "Unique template slug" },
|
|
242
|
+
title: { type: "object", description: "Localized title, e.g. { en: 'Wiki Template' }" },
|
|
243
|
+
description: { type: "object", description: "Localized description" },
|
|
244
|
+
pageType: { type: "string", description: "Page type target" },
|
|
245
|
+
version: { type: "number", description: "Template version number (default 1)" },
|
|
246
|
+
allowedRoles: { type: "array", description: "Roles allowed to use this template" },
|
|
247
|
+
rolePermissions: { type: "object", description: "Granular template role permissions" },
|
|
248
|
+
approvalRules: { type: "object", description: "Approval workflow configuration" },
|
|
249
|
+
defaultProperties: { type: "object", description: "Default property schema groups" },
|
|
250
|
+
initialBlocks: { type: "array", description: "Starter structured CMS blocks" }
|
|
251
|
+
},
|
|
252
|
+
required: ["slug", "title", "pageType"]
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: "cms_update_template",
|
|
257
|
+
description: "Update an existing page template.",
|
|
258
|
+
inputSchema: {
|
|
259
|
+
type: "object",
|
|
260
|
+
properties: {
|
|
261
|
+
id: { type: "string", description: "Template UUID" },
|
|
262
|
+
slug: { type: "string" },
|
|
263
|
+
title: { type: "object" },
|
|
264
|
+
description: { type: "object" },
|
|
265
|
+
pageType: { type: "string" },
|
|
266
|
+
version: { type: "number" },
|
|
267
|
+
defaultProperties: { type: "object" },
|
|
268
|
+
initialBlocks: { type: "array" },
|
|
269
|
+
rolePermissions: { type: "object" },
|
|
270
|
+
approvalRules: { type: "object" }
|
|
271
|
+
},
|
|
272
|
+
required: ["id"]
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: "cms_delete_template",
|
|
277
|
+
description: "Delete a page template by UUID.",
|
|
278
|
+
inputSchema: {
|
|
279
|
+
type: "object",
|
|
280
|
+
properties: {
|
|
281
|
+
id: { type: "string", description: "Template UUID" }
|
|
282
|
+
},
|
|
283
|
+
required: ["id"]
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
// ─── 4. Taxonomies (Categories & Tags) ──────────────────────────────────────
|
|
288
|
+
{
|
|
289
|
+
name: "cms_list_taxonomies",
|
|
290
|
+
description: "List taxonomy terms (categories, tags, genres, etc.) with usage counts.",
|
|
291
|
+
inputSchema: {
|
|
292
|
+
type: "object",
|
|
293
|
+
properties: {
|
|
294
|
+
taxonomy: { type: "string", description: "Taxonomy type, e.g. 'category', 'tag'" }
|
|
295
|
+
},
|
|
296
|
+
required: ["taxonomy"]
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: "cms_create_taxonomy_term",
|
|
301
|
+
description: "Create a new taxonomy term (category or tag).",
|
|
302
|
+
inputSchema: {
|
|
303
|
+
type: "object",
|
|
304
|
+
properties: {
|
|
305
|
+
taxonomy: { type: "string", description: "'category', 'tag', or custom taxonomy name" },
|
|
306
|
+
slug: { type: "string", description: "Unique slug within taxonomy" },
|
|
307
|
+
label: { type: "object", description: "Localized label or string, e.g. { en: 'Guides' }" },
|
|
308
|
+
description: { type: "object", description: "Localized description" },
|
|
309
|
+
parentId: { type: "string", description: "Optional parent category UUID for hierarchy" },
|
|
310
|
+
displayOrder: { type: "number", description: "Display sort order" }
|
|
311
|
+
},
|
|
312
|
+
required: ["taxonomy", "slug", "label"]
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: "cms_update_taxonomy_term",
|
|
317
|
+
description: "Update an existing taxonomy term by UUID.",
|
|
318
|
+
inputSchema: {
|
|
319
|
+
type: "object",
|
|
320
|
+
properties: {
|
|
321
|
+
id: { type: "string", description: "Taxonomy term UUID" },
|
|
322
|
+
slug: { type: "string" },
|
|
323
|
+
label: { type: "object" },
|
|
324
|
+
description: { type: "object" },
|
|
325
|
+
parentId: { type: "string" },
|
|
326
|
+
displayOrder: { type: "number" }
|
|
327
|
+
},
|
|
328
|
+
required: ["id"]
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: "cms_delete_taxonomy_term",
|
|
333
|
+
description: "Delete a taxonomy term by UUID.",
|
|
334
|
+
inputSchema: {
|
|
335
|
+
type: "object",
|
|
336
|
+
properties: {
|
|
337
|
+
id: { type: "string", description: "Taxonomy term UUID" }
|
|
338
|
+
},
|
|
339
|
+
required: ["id"]
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: "cms_assign_page_taxonomy",
|
|
344
|
+
description: "Assign or unassign taxonomy terms to/from a page.",
|
|
345
|
+
inputSchema: {
|
|
346
|
+
type: "object",
|
|
347
|
+
properties: {
|
|
348
|
+
pageId: { type: "string", description: "Page UUID" },
|
|
349
|
+
termIds: {
|
|
350
|
+
type: "array",
|
|
351
|
+
description: "Array of taxonomy term UUIDs to assign to the page"
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
required: ["pageId", "termIds"]
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
// ─── 5. Bylines & Author Schemas ────────────────────────────────────────────
|
|
359
|
+
{
|
|
360
|
+
name: "cms_list_bylines",
|
|
361
|
+
description: "List all registered author and contributor bylines.",
|
|
362
|
+
inputSchema: {
|
|
363
|
+
type: "object",
|
|
364
|
+
properties: {}
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
name: "cms_get_byline",
|
|
369
|
+
description: "Get a specific byline profile by slug or UUID.",
|
|
370
|
+
inputSchema: {
|
|
371
|
+
type: "object",
|
|
372
|
+
properties: {
|
|
373
|
+
slugOrId: { type: "string", description: "Byline slug or UUID" }
|
|
374
|
+
},
|
|
375
|
+
required: ["slugOrId"]
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
name: "cms_create_byline",
|
|
380
|
+
description: "Create an author, creator, or studio byline profile.",
|
|
381
|
+
inputSchema: {
|
|
382
|
+
type: "object",
|
|
383
|
+
properties: {
|
|
384
|
+
name: { type: "string", description: "Display name" },
|
|
385
|
+
slug: { type: "string", description: "Unique URL slug" },
|
|
386
|
+
websiteUrl: { type: "string", description: "Author website URL" },
|
|
387
|
+
bio: { type: "string", description: "Bio or summary text" },
|
|
388
|
+
avatar: { type: "string", description: "Avatar image URL or asset key" },
|
|
389
|
+
userId: { type: "string", description: "Linked user ID if applicable" },
|
|
390
|
+
socials: {
|
|
391
|
+
type: "object",
|
|
392
|
+
description: "Social links, e.g. { twitter: '...', github: '...' }"
|
|
393
|
+
},
|
|
394
|
+
metadata: { type: "object", description: "Custom author metadata" }
|
|
395
|
+
},
|
|
396
|
+
required: ["name", "slug"]
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: "cms_update_byline",
|
|
401
|
+
description: "Update an existing byline profile.",
|
|
402
|
+
inputSchema: {
|
|
403
|
+
type: "object",
|
|
404
|
+
properties: {
|
|
405
|
+
id: { type: "string", description: "Byline UUID" },
|
|
406
|
+
name: { type: "string" },
|
|
407
|
+
slug: { type: "string" },
|
|
408
|
+
websiteUrl: { type: "string" },
|
|
409
|
+
bio: { type: "string" },
|
|
410
|
+
avatar: { type: "string" },
|
|
411
|
+
userId: { type: "string" },
|
|
412
|
+
socials: { type: "object" },
|
|
413
|
+
metadata: { type: "object" }
|
|
414
|
+
},
|
|
415
|
+
required: ["id"]
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: "cms_delete_byline",
|
|
420
|
+
description: "Delete an author byline profile by UUID.",
|
|
421
|
+
inputSchema: {
|
|
422
|
+
type: "object",
|
|
423
|
+
properties: {
|
|
424
|
+
id: { type: "string", description: "Byline UUID" }
|
|
425
|
+
},
|
|
426
|
+
required: ["id"]
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
|
|
430
|
+
// ─── 6. Dynamic Content Types & E-Commerce Collections ──────────────────────
|
|
431
|
+
{
|
|
432
|
+
name: "cms_list_content_types",
|
|
433
|
+
description: "List all dynamic content types, collections, and e-commerce models.",
|
|
434
|
+
inputSchema: {
|
|
435
|
+
type: "object",
|
|
436
|
+
properties: {}
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: "cms_get_content_type",
|
|
441
|
+
description: "Get dynamic content type definition by slug or UUID.",
|
|
442
|
+
inputSchema: {
|
|
443
|
+
type: "object",
|
|
444
|
+
properties: {
|
|
445
|
+
slugOrId: { type: "string", description: "Content type slug or UUID" }
|
|
446
|
+
},
|
|
447
|
+
required: ["slugOrId"]
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
name: "cms_create_content_type",
|
|
452
|
+
description: "Create a new dynamic collection, content type, or e-commerce schema.",
|
|
453
|
+
inputSchema: {
|
|
454
|
+
type: "object",
|
|
455
|
+
properties: {
|
|
456
|
+
slug: {
|
|
457
|
+
type: "string",
|
|
458
|
+
description: "Unique slug identifier (e.g. 'products', 'characters')"
|
|
459
|
+
},
|
|
460
|
+
name: { type: "object", description: "Localized name, e.g. { en: 'Products' }" },
|
|
461
|
+
description: { type: "object", description: "Localized description" },
|
|
462
|
+
icon: { type: "string", description: "Icon name (e.g. 'i-lucide-shopping-bag')" },
|
|
463
|
+
mode: {
|
|
464
|
+
type: "string",
|
|
465
|
+
description: "'document' (blocks), 'data' (structured table), or 'singleton'"
|
|
466
|
+
},
|
|
467
|
+
fieldSchema: {
|
|
468
|
+
type: "object",
|
|
469
|
+
description: "Dynamic property field schemas and validation rules"
|
|
470
|
+
},
|
|
471
|
+
ecomSettings: {
|
|
472
|
+
type: "object",
|
|
473
|
+
description:
|
|
474
|
+
"E-commerce settings { enabled, currencyDefault, trackInventory, hasVariants, allowDigitalDownloads, stripeSync }"
|
|
475
|
+
},
|
|
476
|
+
initialBlocks: { type: "array", description: "Starter blocks for document mode" },
|
|
477
|
+
isSystem: { type: "boolean", description: "Whether this is a protected system schema" }
|
|
478
|
+
},
|
|
479
|
+
required: ["slug", "name"]
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
name: "cms_update_content_type",
|
|
484
|
+
description: "Update an existing dynamic content type schema or e-commerce settings.",
|
|
485
|
+
inputSchema: {
|
|
486
|
+
type: "object",
|
|
487
|
+
properties: {
|
|
488
|
+
id: { type: "string", description: "Content type UUID" },
|
|
489
|
+
slug: { type: "string" },
|
|
490
|
+
name: { type: "object" },
|
|
491
|
+
description: { type: "object" },
|
|
492
|
+
icon: { type: "string" },
|
|
493
|
+
mode: { type: "string" },
|
|
494
|
+
fieldSchema: { type: "object" },
|
|
495
|
+
ecomSettings: { type: "object" },
|
|
496
|
+
initialBlocks: { type: "array" }
|
|
497
|
+
},
|
|
498
|
+
required: ["id"]
|
|
499
|
+
}
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
name: "cms_delete_content_type",
|
|
503
|
+
description: "Delete a custom dynamic content type by UUID.",
|
|
504
|
+
inputSchema: {
|
|
505
|
+
type: "object",
|
|
506
|
+
properties: {
|
|
507
|
+
id: { type: "string", description: "Content type UUID" }
|
|
508
|
+
},
|
|
509
|
+
required: ["id"]
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
// ─── 7. Site Settings ───────────────────────────────────────────────────────
|
|
50
514
|
{
|
|
51
515
|
name: "cms_get_site_settings",
|
|
52
516
|
description: "Retrieve current dynamic site settings including branding and SEO defaults.",
|
|
@@ -69,6 +533,21 @@ export const CMS_MCP_TOOLS = [
|
|
|
69
533
|
seo: { type: "object" }
|
|
70
534
|
}
|
|
71
535
|
}
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
// ─── 8. Search ──────────────────────────────────────────────────────────────
|
|
539
|
+
{
|
|
540
|
+
name: "cms_search_content",
|
|
541
|
+
description: "Search across active CMS pages by keyword.",
|
|
542
|
+
inputSchema: {
|
|
543
|
+
type: "object",
|
|
544
|
+
properties: {
|
|
545
|
+
query: { type: "string", description: "Search query text" },
|
|
546
|
+
type: { type: "string", description: "Optional filter by page type" },
|
|
547
|
+
limit: { type: "number", description: "Max results to return (default 10)" }
|
|
548
|
+
},
|
|
549
|
+
required: ["query"]
|
|
550
|
+
}
|
|
72
551
|
}
|
|
73
552
|
]
|
|
74
553
|
|
|
@@ -100,10 +579,10 @@ export async function handleCmsMcpRequest(
|
|
|
100
579
|
const { name, arguments: args = {} } = params
|
|
101
580
|
|
|
102
581
|
switch (name) {
|
|
582
|
+
// ─── Pages ───────────────────────────────────────────────────────────────
|
|
103
583
|
case "cms_list_pages": {
|
|
104
584
|
const conditions = [isNull(pages.deletedAt)]
|
|
105
585
|
if (args.type) {
|
|
106
|
-
// eslint-disable-next-line typescript/no-unsafe-type-assertion
|
|
107
586
|
conditions.push(eq(pages.type, args.type as PageType))
|
|
108
587
|
}
|
|
109
588
|
if (!args.includeDrafts) {
|
|
@@ -111,19 +590,24 @@ export async function handleCmsMcpRequest(
|
|
|
111
590
|
}
|
|
112
591
|
|
|
113
592
|
const queryLimit = typeof args.limit === "number" ? args.limit : 20
|
|
593
|
+
const queryOffset = typeof args.offset === "number" ? args.offset : 0
|
|
114
594
|
const rows = await db
|
|
115
595
|
.select({
|
|
116
596
|
id: pages.id,
|
|
117
597
|
slug: pages.slug,
|
|
118
598
|
type: pages.type,
|
|
119
599
|
title: pages.title,
|
|
600
|
+
description: pages.description,
|
|
120
601
|
postedAt: pages.postedAt,
|
|
121
|
-
publishedVersionId: pages.publishedVersionId
|
|
602
|
+
publishedVersionId: pages.publishedVersionId,
|
|
603
|
+
createdAt: pages.createdAt,
|
|
604
|
+
updatedAt: pages.updatedAt
|
|
122
605
|
})
|
|
123
606
|
.from(pages)
|
|
124
607
|
.where(and(...conditions))
|
|
125
|
-
.orderBy(desc(pages.postedAt))
|
|
608
|
+
.orderBy(desc(pages.postedAt), desc(pages.createdAt))
|
|
126
609
|
.limit(queryLimit)
|
|
610
|
+
.offset(queryOffset)
|
|
127
611
|
|
|
128
612
|
return {
|
|
129
613
|
jsonrpc: "2.0",
|
|
@@ -135,25 +619,124 @@ export async function handleCmsMcpRequest(
|
|
|
135
619
|
}
|
|
136
620
|
|
|
137
621
|
case "cms_get_page": {
|
|
622
|
+
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
623
|
+
args.slugOrId
|
|
624
|
+
)
|
|
625
|
+
const condition = isUuid ? eq(pages.id, args.slugOrId) : eq(pages.slug, args.slugOrId)
|
|
626
|
+
|
|
138
627
|
const row = await db
|
|
139
628
|
.select()
|
|
140
629
|
.from(pages)
|
|
141
|
-
.where(and(isNull(pages.deletedAt),
|
|
630
|
+
.where(and(isNull(pages.deletedAt), condition))
|
|
142
631
|
.limit(1)
|
|
143
|
-
|
|
144
632
|
const page = row[0] || null
|
|
633
|
+
if (!page) {
|
|
634
|
+
return {
|
|
635
|
+
jsonrpc: "2.0",
|
|
636
|
+
id,
|
|
637
|
+
error: { code: -32004, message: `Page '${args.slugOrId}' not found` }
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
let draft = null
|
|
642
|
+
try {
|
|
643
|
+
const draftRow = await db
|
|
644
|
+
.select()
|
|
645
|
+
.from(pageDrafts)
|
|
646
|
+
.where(eq(pageDrafts.pageId, page.id))
|
|
647
|
+
.limit(1)
|
|
648
|
+
draft = draftRow[0] || null
|
|
649
|
+
} catch {}
|
|
650
|
+
|
|
145
651
|
return {
|
|
146
652
|
jsonrpc: "2.0",
|
|
147
653
|
id,
|
|
148
|
-
result: {
|
|
149
|
-
content: [{ type: "text", text: JSON.stringify(page, null, 2) }]
|
|
150
|
-
}
|
|
654
|
+
result: { content: [{ type: "text", text: JSON.stringify({ page, draft }, null, 2) }] }
|
|
151
655
|
}
|
|
152
656
|
}
|
|
153
657
|
|
|
154
|
-
case "
|
|
155
|
-
const
|
|
156
|
-
|
|
658
|
+
case "cms_create_page": {
|
|
659
|
+
const titlePayload = typeof args.title === "string" ? { en: args.title } : args.title
|
|
660
|
+
const descPayload =
|
|
661
|
+
typeof args.description === "string" ? { en: args.description } : args.description || null
|
|
662
|
+
const blocksPayload = Array.isArray(args.blocks) ? args.blocks : []
|
|
663
|
+
const propertiesPayload = args.properties || {}
|
|
664
|
+
|
|
665
|
+
const inserted = await db
|
|
666
|
+
.insert(pages)
|
|
667
|
+
.values({
|
|
668
|
+
slug: args.slug,
|
|
669
|
+
type: args.type as PageType,
|
|
670
|
+
templateId: args.templateId || null,
|
|
671
|
+
title: titlePayload,
|
|
672
|
+
description: descPayload,
|
|
673
|
+
tags: args.tags || [],
|
|
674
|
+
authorIds: args.authorIds || [],
|
|
675
|
+
bylines: args.bylines || [],
|
|
676
|
+
content: {
|
|
677
|
+
blocks: blocksPayload,
|
|
678
|
+
properties: propertiesPayload
|
|
679
|
+
}
|
|
680
|
+
})
|
|
681
|
+
.returning()
|
|
682
|
+
|
|
683
|
+
const newPage = inserted[0]
|
|
684
|
+
if (newPage) {
|
|
685
|
+
try {
|
|
686
|
+
await db
|
|
687
|
+
.insert(pageDrafts)
|
|
688
|
+
.values({
|
|
689
|
+
pageId: newPage.id,
|
|
690
|
+
updatedBy: args.authorIds?.[0] || "agent",
|
|
691
|
+
content: { blocks: blocksPayload, properties: propertiesPayload }
|
|
692
|
+
})
|
|
693
|
+
.onConflictDoNothing()
|
|
694
|
+
} catch {}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
return {
|
|
698
|
+
jsonrpc: "2.0",
|
|
699
|
+
id,
|
|
700
|
+
result: { content: [{ type: "text", text: JSON.stringify(newPage, null, 2) }] }
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
case "cms_update_page": {
|
|
705
|
+
const updateData: Record<string, any> = { updatedAt: new Date() }
|
|
706
|
+
if (args.slug) updateData["slug"] = args.slug
|
|
707
|
+
if (args.title)
|
|
708
|
+
updateData["title"] = typeof args.title === "string" ? { en: args.title } : args.title
|
|
709
|
+
if (args.description !== undefined) {
|
|
710
|
+
updateData["description"] =
|
|
711
|
+
typeof args.description === "string" ? { en: args.description } : args.description
|
|
712
|
+
}
|
|
713
|
+
if (args.tags) updateData["tags"] = args.tags
|
|
714
|
+
if (args.bylines) updateData["bylines"] = args.bylines
|
|
715
|
+
|
|
716
|
+
if (args.blocks || args.properties) {
|
|
717
|
+
const current = await db.select().from(pages).where(eq(pages.id, args.id)).limit(1)
|
|
718
|
+
const existingContent = current[0]?.content || { blocks: [], properties: {} }
|
|
719
|
+
updateData["content"] = {
|
|
720
|
+
blocks: args.blocks || existingContent.blocks || [],
|
|
721
|
+
properties: args.properties || existingContent.properties || {}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const updated = await db
|
|
726
|
+
.update(pages)
|
|
727
|
+
.set(updateData)
|
|
728
|
+
.where(eq(pages.id, args.id))
|
|
729
|
+
.returning()
|
|
730
|
+
return {
|
|
731
|
+
jsonrpc: "2.0",
|
|
732
|
+
id,
|
|
733
|
+
result: { content: [{ type: "text", text: JSON.stringify(updated[0] || null, null, 2) }] }
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
case "cms_create_draft": {
|
|
738
|
+
const contentPayload = {
|
|
739
|
+
blocks: args.blocks as BaseBlock[],
|
|
157
740
|
properties: args.properties || {}
|
|
158
741
|
}
|
|
159
742
|
|
|
@@ -174,55 +757,730 @@ export async function handleCmsMcpRequest(
|
|
|
174
757
|
})
|
|
175
758
|
.returning()
|
|
176
759
|
|
|
760
|
+
return {
|
|
761
|
+
jsonrpc: "2.0",
|
|
762
|
+
id,
|
|
763
|
+
result: { content: [{ type: "text", text: JSON.stringify(draft[0], null, 2) }] }
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
case "cms_publish_page": {
|
|
768
|
+
const pageResult = await db.select().from(pages).where(eq(pages.id, args.pageId)).limit(1)
|
|
769
|
+
const page = pageResult[0]
|
|
770
|
+
if (!page) {
|
|
771
|
+
return {
|
|
772
|
+
jsonrpc: "2.0",
|
|
773
|
+
id,
|
|
774
|
+
error: { code: -32004, message: `Page '${args.pageId}' not found` }
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
const draftResult = await db
|
|
779
|
+
.select()
|
|
780
|
+
.from(pageDrafts)
|
|
781
|
+
.where(eq(pageDrafts.pageId, args.pageId))
|
|
782
|
+
.limit(1)
|
|
783
|
+
const content = draftResult[0]?.content || page.content
|
|
784
|
+
|
|
785
|
+
const lastVersion = await db
|
|
786
|
+
.select()
|
|
787
|
+
.from(pageVersions)
|
|
788
|
+
.where(eq(pageVersions.pageId, args.pageId))
|
|
789
|
+
.orderBy(desc(pageVersions.versionNumber))
|
|
790
|
+
.limit(1)
|
|
791
|
+
|
|
792
|
+
const nextVersionNumber = (lastVersion[0]?.versionNumber || 0) + 1
|
|
793
|
+
|
|
794
|
+
const versionResult = await db
|
|
795
|
+
.insert(pageVersions)
|
|
796
|
+
.values({
|
|
797
|
+
pageId: args.pageId,
|
|
798
|
+
versionNumber: nextVersionNumber,
|
|
799
|
+
status: "published",
|
|
800
|
+
slug: page.slug,
|
|
801
|
+
type: page.type,
|
|
802
|
+
title: page.title,
|
|
803
|
+
description: page.description,
|
|
804
|
+
tags: page.tags,
|
|
805
|
+
authorIds: page.authorIds,
|
|
806
|
+
bylines: page.bylines,
|
|
807
|
+
content,
|
|
808
|
+
createdBy: args.userId,
|
|
809
|
+
approvedBy: [args.userId],
|
|
810
|
+
approvedAt: new Date(),
|
|
811
|
+
changeSummary: args.changeSummary || "Published via MCP",
|
|
812
|
+
createdAt: new Date()
|
|
813
|
+
})
|
|
814
|
+
.returning()
|
|
815
|
+
|
|
816
|
+
const version = versionResult[0]
|
|
817
|
+
|
|
818
|
+
await db
|
|
819
|
+
.update(pages)
|
|
820
|
+
.set({
|
|
821
|
+
content,
|
|
822
|
+
publishedVersionId: version.id,
|
|
823
|
+
postedAt: new Date(),
|
|
824
|
+
updatedAt: new Date()
|
|
825
|
+
})
|
|
826
|
+
.where(eq(pages.id, args.pageId))
|
|
827
|
+
|
|
828
|
+
await indexPageForSearch(db, { id: args.pageId, title: page.title, content }, ["en", "pt"])
|
|
829
|
+
|
|
830
|
+
return {
|
|
831
|
+
jsonrpc: "2.0",
|
|
832
|
+
id,
|
|
833
|
+
result: { content: [{ type: "text", text: JSON.stringify(version, null, 2) }] }
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
case "cms_unpublish_page": {
|
|
838
|
+
const updated = await db
|
|
839
|
+
.update(pages)
|
|
840
|
+
.set({ postedAt: null, publishedVersionId: null, updatedAt: new Date() })
|
|
841
|
+
.where(eq(pages.id, args.pageId))
|
|
842
|
+
.returning()
|
|
843
|
+
|
|
177
844
|
return {
|
|
178
845
|
jsonrpc: "2.0",
|
|
179
846
|
id,
|
|
180
847
|
result: {
|
|
181
|
-
content: [
|
|
848
|
+
content: [
|
|
849
|
+
{
|
|
850
|
+
type: "text",
|
|
851
|
+
text: JSON.stringify({ success: true, page: updated[0] || null }, null, 2)
|
|
852
|
+
}
|
|
853
|
+
]
|
|
182
854
|
}
|
|
183
855
|
}
|
|
184
856
|
}
|
|
185
857
|
|
|
186
|
-
case "
|
|
187
|
-
const
|
|
858
|
+
case "cms_delete_page": {
|
|
859
|
+
const deleted = await db
|
|
860
|
+
.update(pages)
|
|
861
|
+
.set({ deletedAt: new Date() })
|
|
862
|
+
.where(eq(pages.id, args.pageId))
|
|
863
|
+
.returning()
|
|
864
|
+
|
|
188
865
|
return {
|
|
189
866
|
jsonrpc: "2.0",
|
|
190
867
|
id,
|
|
191
868
|
result: {
|
|
192
|
-
content: [
|
|
869
|
+
content: [
|
|
870
|
+
{
|
|
871
|
+
type: "text",
|
|
872
|
+
text: JSON.stringify({ success: true, page: deleted[0] || null }, null, 2)
|
|
873
|
+
}
|
|
874
|
+
]
|
|
193
875
|
}
|
|
194
876
|
}
|
|
195
877
|
}
|
|
196
878
|
|
|
197
|
-
|
|
198
|
-
|
|
879
|
+
// ─── Versions & Reviews ───────────────────────────────────────────────────
|
|
880
|
+
case "cms_list_page_versions": {
|
|
881
|
+
const vLimit = typeof args.limit === "number" ? args.limit : 20
|
|
882
|
+
const rows = await db
|
|
883
|
+
.select()
|
|
884
|
+
.from(pageVersions)
|
|
885
|
+
.where(eq(pageVersions.pageId, args.pageId))
|
|
886
|
+
.orderBy(desc(pageVersions.versionNumber))
|
|
887
|
+
.limit(vLimit)
|
|
888
|
+
|
|
889
|
+
return {
|
|
890
|
+
jsonrpc: "2.0",
|
|
891
|
+
id,
|
|
892
|
+
result: { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] }
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
case "cms_get_page_version": {
|
|
897
|
+
const row = await db
|
|
898
|
+
.select()
|
|
899
|
+
.from(pageVersions)
|
|
900
|
+
.where(eq(pageVersions.id, args.versionId))
|
|
901
|
+
.limit(1)
|
|
902
|
+
return {
|
|
903
|
+
jsonrpc: "2.0",
|
|
904
|
+
id,
|
|
905
|
+
result: { content: [{ type: "text", text: JSON.stringify(row[0] || null, null, 2) }] }
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
case "cms_rollback_page_version": {
|
|
910
|
+
const vRow = await db
|
|
911
|
+
.select()
|
|
912
|
+
.from(pageVersions)
|
|
913
|
+
.where(eq(pageVersions.id, args.versionId))
|
|
914
|
+
.limit(1)
|
|
915
|
+
const targetVersion = vRow[0]
|
|
916
|
+
if (!targetVersion) {
|
|
917
|
+
return {
|
|
918
|
+
jsonrpc: "2.0",
|
|
919
|
+
id,
|
|
920
|
+
error: { code: -32004, message: "Version snapshot not found" }
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
const draft = await db
|
|
925
|
+
.insert(pageDrafts)
|
|
926
|
+
.values({
|
|
927
|
+
pageId: args.pageId,
|
|
928
|
+
updatedBy: args.userId,
|
|
929
|
+
content: targetVersion.content
|
|
930
|
+
})
|
|
931
|
+
.onConflictDoUpdate({
|
|
932
|
+
target: [pageDrafts.pageId],
|
|
933
|
+
set: {
|
|
934
|
+
content: targetVersion.content,
|
|
935
|
+
updatedBy: args.userId,
|
|
936
|
+
updatedAt: new Date()
|
|
937
|
+
}
|
|
938
|
+
})
|
|
939
|
+
.returning()
|
|
940
|
+
|
|
199
941
|
return {
|
|
200
942
|
jsonrpc: "2.0",
|
|
201
943
|
id,
|
|
202
944
|
result: {
|
|
203
|
-
content: [
|
|
945
|
+
content: [
|
|
946
|
+
{
|
|
947
|
+
type: "text",
|
|
948
|
+
text: JSON.stringify(
|
|
949
|
+
{
|
|
950
|
+
success: true,
|
|
951
|
+
rolledBackToVersion: targetVersion.versionNumber,
|
|
952
|
+
draft: draft[0]
|
|
953
|
+
},
|
|
954
|
+
null,
|
|
955
|
+
2
|
|
956
|
+
)
|
|
957
|
+
}
|
|
958
|
+
]
|
|
204
959
|
}
|
|
205
960
|
}
|
|
206
961
|
}
|
|
207
962
|
|
|
208
|
-
|
|
963
|
+
case "cms_approve_page_version": {
|
|
964
|
+
const approval = await db
|
|
965
|
+
.insert(pageVersionApprovals)
|
|
966
|
+
.values({
|
|
967
|
+
versionId: args.versionId,
|
|
968
|
+
userId: args.userId,
|
|
969
|
+
userRole: args.userRole,
|
|
970
|
+
approvedAt: new Date()
|
|
971
|
+
})
|
|
972
|
+
.returning()
|
|
973
|
+
|
|
974
|
+
return {
|
|
975
|
+
jsonrpc: "2.0",
|
|
976
|
+
id,
|
|
977
|
+
result: { content: [{ type: "text", text: JSON.stringify(approval[0], null, 2) }] }
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
case "cms_add_version_comment": {
|
|
982
|
+
const comment = await db
|
|
983
|
+
.insert(pageVersionComments)
|
|
984
|
+
.values({
|
|
985
|
+
versionId: args.versionId,
|
|
986
|
+
userId: args.userId,
|
|
987
|
+
userRole: args.userRole,
|
|
988
|
+
content: args.content,
|
|
989
|
+
blockId: args.blockId || null,
|
|
990
|
+
createdAt: new Date()
|
|
991
|
+
})
|
|
992
|
+
.returning()
|
|
993
|
+
|
|
994
|
+
return {
|
|
995
|
+
jsonrpc: "2.0",
|
|
996
|
+
id,
|
|
997
|
+
result: { content: [{ type: "text", text: JSON.stringify(comment[0], null, 2) }] }
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
// ─── Templates ───────────────────────────────────────────────────────────
|
|
1002
|
+
case "cms_list_templates": {
|
|
1003
|
+
const conditions = []
|
|
1004
|
+
if (args.pageType) {
|
|
1005
|
+
conditions.push(eq(pageTemplates.pageType, args.pageType))
|
|
1006
|
+
}
|
|
1007
|
+
const rows = await db
|
|
1008
|
+
.select()
|
|
1009
|
+
.from(pageTemplates)
|
|
1010
|
+
.where(conditions.length > 0 ? and(...conditions) : undefined)
|
|
1011
|
+
.orderBy(desc(pageTemplates.createdAt))
|
|
1012
|
+
|
|
1013
|
+
return {
|
|
1014
|
+
jsonrpc: "2.0",
|
|
1015
|
+
id,
|
|
1016
|
+
result: { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] }
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
case "cms_get_template": {
|
|
1021
|
+
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
1022
|
+
args.slugOrId
|
|
1023
|
+
)
|
|
1024
|
+
const condition = isUuid
|
|
1025
|
+
? eq(pageTemplates.id, args.slugOrId)
|
|
1026
|
+
: eq(pageTemplates.slug, args.slugOrId)
|
|
1027
|
+
const row = await db.select().from(pageTemplates).where(condition).limit(1)
|
|
1028
|
+
|
|
1029
|
+
return {
|
|
1030
|
+
jsonrpc: "2.0",
|
|
1031
|
+
id,
|
|
1032
|
+
result: { content: [{ type: "text", text: JSON.stringify(row[0] || null, null, 2) }] }
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
case "cms_create_template": {
|
|
1037
|
+
const titlePayload = typeof args.title === "string" ? { en: args.title } : args.title
|
|
1038
|
+
const descPayload =
|
|
1039
|
+
typeof args.description === "string" ? { en: args.description } : args.description || null
|
|
1040
|
+
|
|
1041
|
+
const tmpl = await db
|
|
1042
|
+
.insert(pageTemplates)
|
|
1043
|
+
.values({
|
|
1044
|
+
slug: args.slug,
|
|
1045
|
+
title: titlePayload,
|
|
1046
|
+
description: descPayload,
|
|
1047
|
+
pageType: args.pageType,
|
|
1048
|
+
version: args.version || 1,
|
|
1049
|
+
allowedRoles: args.allowedRoles || [],
|
|
1050
|
+
rolePermissions: args.rolePermissions || {
|
|
1051
|
+
whoCanCreate: [],
|
|
1052
|
+
whoCanEdit: [],
|
|
1053
|
+
whoCanReview: [],
|
|
1054
|
+
whoCanView: []
|
|
1055
|
+
},
|
|
1056
|
+
approvalRules: args.approvalRules || { allowSelfApproval: true, minApprovals: 1 },
|
|
1057
|
+
defaultProperties: args.defaultProperties || {},
|
|
1058
|
+
initialBlocks: args.initialBlocks || []
|
|
1059
|
+
})
|
|
1060
|
+
.returning()
|
|
1061
|
+
|
|
1062
|
+
return {
|
|
1063
|
+
jsonrpc: "2.0",
|
|
1064
|
+
id,
|
|
1065
|
+
result: { content: [{ type: "text", text: JSON.stringify(tmpl[0], null, 2) }] }
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
case "cms_update_template": {
|
|
1070
|
+
const updateData: Record<string, any> = { updatedAt: new Date() }
|
|
1071
|
+
if (args.slug) updateData["slug"] = args.slug
|
|
1072
|
+
if (args.title)
|
|
1073
|
+
updateData["title"] = typeof args.title === "string" ? { en: args.title } : args.title
|
|
1074
|
+
if (args.description !== undefined) {
|
|
1075
|
+
updateData["description"] =
|
|
1076
|
+
typeof args.description === "string" ? { en: args.description } : args.description
|
|
1077
|
+
}
|
|
1078
|
+
if (args.pageType) updateData["pageType"] = args.pageType
|
|
1079
|
+
if (args.version) updateData["version"] = args.version
|
|
1080
|
+
if (args.defaultProperties) updateData["defaultProperties"] = args.defaultProperties
|
|
1081
|
+
if (args.initialBlocks) updateData["initialBlocks"] = args.initialBlocks
|
|
1082
|
+
if (args.rolePermissions) updateData["rolePermissions"] = args.rolePermissions
|
|
1083
|
+
if (args.approvalRules) updateData["approvalRules"] = args.approvalRules
|
|
1084
|
+
|
|
1085
|
+
const updated = await db
|
|
1086
|
+
.update(pageTemplates)
|
|
1087
|
+
.set(updateData)
|
|
1088
|
+
.where(eq(pageTemplates.id, args.id))
|
|
1089
|
+
.returning()
|
|
1090
|
+
return {
|
|
1091
|
+
jsonrpc: "2.0",
|
|
1092
|
+
id,
|
|
1093
|
+
result: { content: [{ type: "text", text: JSON.stringify(updated[0] || null, null, 2) }] }
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
case "cms_delete_template": {
|
|
1098
|
+
const deleted = await db
|
|
1099
|
+
.delete(pageTemplates)
|
|
1100
|
+
.where(eq(pageTemplates.id, args.id))
|
|
1101
|
+
.returning()
|
|
1102
|
+
return {
|
|
1103
|
+
jsonrpc: "2.0",
|
|
1104
|
+
id,
|
|
1105
|
+
result: {
|
|
1106
|
+
content: [
|
|
1107
|
+
{
|
|
1108
|
+
type: "text",
|
|
1109
|
+
text: JSON.stringify({ success: true, template: deleted[0] || null }, null, 2)
|
|
1110
|
+
}
|
|
1111
|
+
]
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// ─── Taxonomies ───────────────────────────────────────────────────────────
|
|
1117
|
+
case "cms_list_taxonomies": {
|
|
1118
|
+
const terms = await getTaxonomyTerms(db, args.taxonomy)
|
|
209
1119
|
return {
|
|
210
1120
|
jsonrpc: "2.0",
|
|
211
1121
|
id,
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
1122
|
+
result: { content: [{ type: "text", text: JSON.stringify(terms, null, 2) }] }
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
case "cms_create_taxonomy_term": {
|
|
1127
|
+
const labelPayload = typeof args.label === "string" ? { en: args.label } : args.label
|
|
1128
|
+
const descPayload =
|
|
1129
|
+
typeof args.description === "string" ? { en: args.description } : args.description || null
|
|
1130
|
+
|
|
1131
|
+
const term = await db
|
|
1132
|
+
.insert(taxonomyTerms)
|
|
1133
|
+
.values({
|
|
1134
|
+
taxonomy: args.taxonomy,
|
|
1135
|
+
slug: args.slug,
|
|
1136
|
+
label: labelPayload,
|
|
1137
|
+
description: descPayload,
|
|
1138
|
+
parentId: args.parentId || null,
|
|
1139
|
+
displayOrder: args.displayOrder || 0
|
|
1140
|
+
})
|
|
1141
|
+
.returning()
|
|
1142
|
+
|
|
1143
|
+
return {
|
|
1144
|
+
jsonrpc: "2.0",
|
|
1145
|
+
id,
|
|
1146
|
+
result: { content: [{ type: "text", text: JSON.stringify(term[0], null, 2) }] }
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
case "cms_update_taxonomy_term": {
|
|
1151
|
+
const updateData: Record<string, any> = { updatedAt: new Date() }
|
|
1152
|
+
if (args.slug) updateData["slug"] = args.slug
|
|
1153
|
+
if (args.label)
|
|
1154
|
+
updateData["label"] = typeof args.label === "string" ? { en: args.label } : args.label
|
|
1155
|
+
if (args.description !== undefined) {
|
|
1156
|
+
updateData["description"] =
|
|
1157
|
+
typeof args.description === "string" ? { en: args.description } : args.description
|
|
1158
|
+
}
|
|
1159
|
+
if (args.parentId !== undefined) updateData["parentId"] = args.parentId || null
|
|
1160
|
+
if (args.displayOrder !== undefined) updateData["displayOrder"] = args.displayOrder
|
|
1161
|
+
|
|
1162
|
+
const updated = await db
|
|
1163
|
+
.update(taxonomyTerms)
|
|
1164
|
+
.set(updateData)
|
|
1165
|
+
.where(eq(taxonomyTerms.id, args.id))
|
|
1166
|
+
.returning()
|
|
1167
|
+
return {
|
|
1168
|
+
jsonrpc: "2.0",
|
|
1169
|
+
id,
|
|
1170
|
+
result: { content: [{ type: "text", text: JSON.stringify(updated[0] || null, null, 2) }] }
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
case "cms_delete_taxonomy_term": {
|
|
1175
|
+
const deleted = await db
|
|
1176
|
+
.delete(taxonomyTerms)
|
|
1177
|
+
.where(eq(taxonomyTerms.id, args.id))
|
|
1178
|
+
.returning()
|
|
1179
|
+
return {
|
|
1180
|
+
jsonrpc: "2.0",
|
|
1181
|
+
id,
|
|
1182
|
+
result: {
|
|
1183
|
+
content: [
|
|
1184
|
+
{
|
|
1185
|
+
type: "text",
|
|
1186
|
+
text: JSON.stringify({ success: true, term: deleted[0] || null }, null, 2)
|
|
1187
|
+
}
|
|
1188
|
+
]
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
case "cms_assign_page_taxonomy": {
|
|
1194
|
+
// Clear current assignments and re-assign
|
|
1195
|
+
await db.delete(pageTaxonomyTerms).where(eq(pageTaxonomyTerms.pageId, args.pageId))
|
|
1196
|
+
const termIds = Array.isArray(args.termIds) ? args.termIds : []
|
|
1197
|
+
const assigned = []
|
|
1198
|
+
for (const termId of termIds) {
|
|
1199
|
+
const row = await db
|
|
1200
|
+
.insert(pageTaxonomyTerms)
|
|
1201
|
+
.values({ pageId: args.pageId, termId })
|
|
1202
|
+
.returning()
|
|
1203
|
+
assigned.push(row[0])
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
return {
|
|
1207
|
+
jsonrpc: "2.0",
|
|
1208
|
+
id,
|
|
1209
|
+
result: {
|
|
1210
|
+
content: [
|
|
1211
|
+
{
|
|
1212
|
+
type: "text",
|
|
1213
|
+
text: JSON.stringify({ success: true, assignedCount: assigned.length }, null, 2)
|
|
1214
|
+
}
|
|
1215
|
+
]
|
|
215
1216
|
}
|
|
216
1217
|
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
// ─── Bylines ─────────────────────────────────────────────────────────────
|
|
1221
|
+
case "cms_list_bylines": {
|
|
1222
|
+
const rows = await db
|
|
1223
|
+
.select()
|
|
1224
|
+
.from(bylines)
|
|
1225
|
+
.where(isNull(bylines.deletedAt))
|
|
1226
|
+
.orderBy(desc(bylines.createdAt))
|
|
1227
|
+
|
|
1228
|
+
return {
|
|
1229
|
+
jsonrpc: "2.0",
|
|
1230
|
+
id,
|
|
1231
|
+
result: { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] }
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
case "cms_get_byline": {
|
|
1236
|
+
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
1237
|
+
args.slugOrId
|
|
1238
|
+
)
|
|
1239
|
+
const condition = isUuid ? eq(bylines.id, args.slugOrId) : eq(bylines.slug, args.slugOrId)
|
|
1240
|
+
const row = await db
|
|
1241
|
+
.select()
|
|
1242
|
+
.from(bylines)
|
|
1243
|
+
.where(and(isNull(bylines.deletedAt), condition))
|
|
1244
|
+
.limit(1)
|
|
1245
|
+
|
|
1246
|
+
return {
|
|
1247
|
+
jsonrpc: "2.0",
|
|
1248
|
+
id,
|
|
1249
|
+
result: { content: [{ type: "text", text: JSON.stringify(row[0] || null, null, 2) }] }
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
case "cms_create_byline": {
|
|
1254
|
+
const row = await db
|
|
1255
|
+
.insert(bylines)
|
|
1256
|
+
.values({
|
|
1257
|
+
name: args.name,
|
|
1258
|
+
slug: args.slug,
|
|
1259
|
+
websiteUrl: args.websiteUrl || null,
|
|
1260
|
+
bio: args.bio || null,
|
|
1261
|
+
avatar: args.avatar || null,
|
|
1262
|
+
userId: args.userId || null,
|
|
1263
|
+
socials: args.socials || {},
|
|
1264
|
+
metadata: args.metadata || {}
|
|
1265
|
+
})
|
|
1266
|
+
.returning()
|
|
1267
|
+
|
|
1268
|
+
return {
|
|
1269
|
+
jsonrpc: "2.0",
|
|
1270
|
+
id,
|
|
1271
|
+
result: { content: [{ type: "text", text: JSON.stringify(row[0], null, 2) }] }
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
case "cms_update_byline": {
|
|
1276
|
+
const updateData: Record<string, any> = { updatedAt: new Date() }
|
|
1277
|
+
if (args.name) updateData["name"] = args.name
|
|
1278
|
+
if (args.slug) updateData["slug"] = args.slug
|
|
1279
|
+
if (args.websiteUrl !== undefined) updateData["websiteUrl"] = args.websiteUrl
|
|
1280
|
+
if (args.bio !== undefined) updateData["bio"] = args.bio
|
|
1281
|
+
if (args.avatar !== undefined) updateData["avatar"] = args.avatar
|
|
1282
|
+
if (args.userId !== undefined) updateData["userId"] = args.userId
|
|
1283
|
+
if (args.socials) updateData["socials"] = args.socials
|
|
1284
|
+
if (args.metadata) updateData["metadata"] = args.metadata
|
|
1285
|
+
|
|
1286
|
+
const updated = await db
|
|
1287
|
+
.update(bylines)
|
|
1288
|
+
.set(updateData)
|
|
1289
|
+
.where(eq(bylines.id, args.id))
|
|
1290
|
+
.returning()
|
|
1291
|
+
return {
|
|
1292
|
+
jsonrpc: "2.0",
|
|
1293
|
+
id,
|
|
1294
|
+
result: { content: [{ type: "text", text: JSON.stringify(updated[0] || null, null, 2) }] }
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
case "cms_delete_byline": {
|
|
1299
|
+
const deleted = await db
|
|
1300
|
+
.update(bylines)
|
|
1301
|
+
.set({ deletedAt: new Date() })
|
|
1302
|
+
.where(eq(bylines.id, args.id))
|
|
1303
|
+
.returning()
|
|
1304
|
+
return {
|
|
1305
|
+
jsonrpc: "2.0",
|
|
1306
|
+
id,
|
|
1307
|
+
result: {
|
|
1308
|
+
content: [
|
|
1309
|
+
{
|
|
1310
|
+
type: "text",
|
|
1311
|
+
text: JSON.stringify({ success: true, byline: deleted[0] || null }, null, 2)
|
|
1312
|
+
}
|
|
1313
|
+
]
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
// ─── Dynamic Content Types & E-Commerce ───────────────────────────────────
|
|
1319
|
+
case "cms_list_content_types": {
|
|
1320
|
+
const rows = await db.select().from(contentTypes).orderBy(desc(contentTypes.createdAt))
|
|
1321
|
+
return {
|
|
1322
|
+
jsonrpc: "2.0",
|
|
1323
|
+
id,
|
|
1324
|
+
result: { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] }
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
case "cms_get_content_type": {
|
|
1329
|
+
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
1330
|
+
args.slugOrId
|
|
1331
|
+
)
|
|
1332
|
+
const condition = isUuid
|
|
1333
|
+
? eq(contentTypes.id, args.slugOrId)
|
|
1334
|
+
: eq(contentTypes.slug, args.slugOrId)
|
|
1335
|
+
const row = await db.select().from(contentTypes).where(condition).limit(1)
|
|
1336
|
+
|
|
1337
|
+
return {
|
|
1338
|
+
jsonrpc: "2.0",
|
|
1339
|
+
id,
|
|
1340
|
+
result: { content: [{ type: "text", text: JSON.stringify(row[0] || null, null, 2) }] }
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
case "cms_create_content_type": {
|
|
1345
|
+
const namePayload = typeof args.name === "string" ? { en: args.name } : args.name
|
|
1346
|
+
const descPayload =
|
|
1347
|
+
typeof args.description === "string" ? { en: args.description } : args.description || null
|
|
1348
|
+
|
|
1349
|
+
const row = await db
|
|
1350
|
+
.insert(contentTypes)
|
|
1351
|
+
.values({
|
|
1352
|
+
slug: args.slug,
|
|
1353
|
+
name: namePayload,
|
|
1354
|
+
description: descPayload,
|
|
1355
|
+
icon: args.icon || "i-lucide-box",
|
|
1356
|
+
mode: args.mode || "document",
|
|
1357
|
+
fieldSchema: args.fieldSchema || {},
|
|
1358
|
+
ecomSettings: args.ecomSettings || null,
|
|
1359
|
+
initialBlocks: args.initialBlocks || [],
|
|
1360
|
+
isSystem: Boolean(args.isSystem)
|
|
1361
|
+
})
|
|
1362
|
+
.returning()
|
|
1363
|
+
|
|
1364
|
+
return {
|
|
1365
|
+
jsonrpc: "2.0",
|
|
1366
|
+
id,
|
|
1367
|
+
result: { content: [{ type: "text", text: JSON.stringify(row[0], null, 2) }] }
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
case "cms_update_content_type": {
|
|
1372
|
+
const updateData: Record<string, any> = { updatedAt: new Date() }
|
|
1373
|
+
if (args.slug) updateData["slug"] = args.slug
|
|
1374
|
+
if (args.name)
|
|
1375
|
+
updateData["name"] = typeof args.name === "string" ? { en: args.name } : args.name
|
|
1376
|
+
if (args.description !== undefined) {
|
|
1377
|
+
updateData["description"] =
|
|
1378
|
+
typeof args.description === "string" ? { en: args.description } : args.description
|
|
1379
|
+
}
|
|
1380
|
+
if (args.icon) updateData["icon"] = args.icon
|
|
1381
|
+
if (args.mode) updateData["mode"] = args.mode
|
|
1382
|
+
if (args.fieldSchema) updateData["fieldSchema"] = args.fieldSchema
|
|
1383
|
+
if (args.ecomSettings !== undefined) updateData["ecomSettings"] = args.ecomSettings
|
|
1384
|
+
if (args.initialBlocks) updateData["initialBlocks"] = args.initialBlocks
|
|
1385
|
+
|
|
1386
|
+
const updated = await db
|
|
1387
|
+
.update(contentTypes)
|
|
1388
|
+
.set(updateData)
|
|
1389
|
+
.where(eq(contentTypes.id, args.id))
|
|
1390
|
+
.returning()
|
|
1391
|
+
return {
|
|
1392
|
+
jsonrpc: "2.0",
|
|
1393
|
+
id,
|
|
1394
|
+
result: { content: [{ type: "text", text: JSON.stringify(updated[0] || null, null, 2) }] }
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
case "cms_delete_content_type": {
|
|
1399
|
+
const deleted = await db
|
|
1400
|
+
.delete(contentTypes)
|
|
1401
|
+
.where(eq(contentTypes.id, args.id))
|
|
1402
|
+
.returning()
|
|
1403
|
+
return {
|
|
1404
|
+
jsonrpc: "2.0",
|
|
1405
|
+
id,
|
|
1406
|
+
result: {
|
|
1407
|
+
content: [
|
|
1408
|
+
{
|
|
1409
|
+
type: "text",
|
|
1410
|
+
text: JSON.stringify({ success: true, contentType: deleted[0] || null }, null, 2)
|
|
1411
|
+
}
|
|
1412
|
+
]
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// ─── Site Settings ────────────────────────────────────────────────────────
|
|
1418
|
+
case "cms_get_site_settings": {
|
|
1419
|
+
const settings = await getSiteSettings(db)
|
|
1420
|
+
return {
|
|
1421
|
+
jsonrpc: "2.0",
|
|
1422
|
+
id,
|
|
1423
|
+
result: { content: [{ type: "text", text: JSON.stringify(settings, null, 2) }] }
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
case "cms_update_site_settings": {
|
|
1428
|
+
const updated = await updateSiteSettings(db, args)
|
|
1429
|
+
return {
|
|
1430
|
+
jsonrpc: "2.0",
|
|
1431
|
+
id,
|
|
1432
|
+
result: { content: [{ type: "text", text: JSON.stringify(updated, null, 2) }] }
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// ─── Search ───────────────────────────────────────────────────────────────
|
|
1437
|
+
case "cms_search_content": {
|
|
1438
|
+
const queryLimit = typeof args.limit === "number" ? args.limit : 10
|
|
1439
|
+
const conditions = [isNull(pages.deletedAt)]
|
|
1440
|
+
if (args.type) {
|
|
1441
|
+
conditions.push(eq(pages.type, args.type as PageType))
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
const rows = await db
|
|
1445
|
+
.select({
|
|
1446
|
+
id: pages.id,
|
|
1447
|
+
slug: pages.slug,
|
|
1448
|
+
type: pages.type,
|
|
1449
|
+
title: pages.title,
|
|
1450
|
+
description: pages.description,
|
|
1451
|
+
postedAt: pages.postedAt
|
|
1452
|
+
})
|
|
1453
|
+
.from(pages)
|
|
1454
|
+
.where(
|
|
1455
|
+
and(
|
|
1456
|
+
...conditions,
|
|
1457
|
+
or(
|
|
1458
|
+
like(pages.slug, `%${args.query}%`),
|
|
1459
|
+
sql`CAST(${pages.title} AS TEXT) ILIKE ${`%${args.query}%`}`
|
|
1460
|
+
)
|
|
1461
|
+
)
|
|
1462
|
+
)
|
|
1463
|
+
.limit(queryLimit)
|
|
1464
|
+
|
|
1465
|
+
return {
|
|
1466
|
+
jsonrpc: "2.0",
|
|
1467
|
+
id,
|
|
1468
|
+
result: { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] }
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
default:
|
|
1473
|
+
return {
|
|
1474
|
+
jsonrpc: "2.0",
|
|
1475
|
+
id,
|
|
1476
|
+
error: { code: -32601, message: `Method or tool '${name}' not found` }
|
|
1477
|
+
}
|
|
217
1478
|
}
|
|
218
1479
|
}
|
|
219
1480
|
|
|
220
1481
|
return {
|
|
221
1482
|
jsonrpc: "2.0",
|
|
222
1483
|
id,
|
|
223
|
-
error: {
|
|
224
|
-
code: -32601,
|
|
225
|
-
message: `Unknown method: ${method}`
|
|
226
|
-
}
|
|
1484
|
+
error: { code: -32601, message: `Unknown method: ${method}` }
|
|
227
1485
|
}
|
|
228
1486
|
}
|