@jgardner04/ghost-mcp-server 1.11.0 → 1.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +199 -43
- package/package.json +2 -1
- package/src/__tests__/mcp_server.test.js +10 -4
- package/src/__tests__/mcp_server_improved.test.js +261 -156
- package/src/__tests__/mcp_server_pages.test.js +72 -68
- package/src/errors/__tests__/index.test.js +70 -0
- package/src/errors/index.js +10 -0
- package/src/mcp_server.js +9 -19
- package/src/mcp_server_improved.js +716 -531
- package/src/schemas/__tests__/common.test.js +84 -0
- package/src/schemas/__tests__/memberSchemas.test.js +447 -0
- package/src/schemas/__tests__/newsletterSchemas.test.js +399 -0
- package/src/schemas/__tests__/pageSchemas.test.js +518 -0
- package/src/schemas/__tests__/tierSchemas.test.js +574 -0
- package/src/schemas/common.js +50 -3
- package/src/services/__tests__/ghostServiceImproved.members.test.js +12 -61
- package/src/services/__tests__/postService.test.js +7 -99
- package/src/services/ghostServiceImproved.js +10 -21
- package/src/services/postService.js +4 -30
- package/src/utils/__tests__/tempFileManager.test.js +316 -0
- package/src/utils/__tests__/validation.test.js +163 -0
- package/src/utils/tempFileManager.js +113 -0
- package/src/utils/validation.js +28 -0
|
@@ -8,6 +8,31 @@ import fs from 'fs';
|
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import os from 'os';
|
|
10
10
|
import crypto from 'crypto';
|
|
11
|
+
import { ValidationError } from './errors/index.js';
|
|
12
|
+
import { validateToolInput } from './utils/validation.js';
|
|
13
|
+
import { trackTempFile, cleanupTempFiles } from './utils/tempFileManager.js';
|
|
14
|
+
import {
|
|
15
|
+
createTagSchema,
|
|
16
|
+
updateTagSchema,
|
|
17
|
+
tagQuerySchema,
|
|
18
|
+
ghostIdSchema,
|
|
19
|
+
emailSchema,
|
|
20
|
+
createPostSchema,
|
|
21
|
+
updatePostSchema,
|
|
22
|
+
postQuerySchema,
|
|
23
|
+
createMemberSchema,
|
|
24
|
+
updateMemberSchema,
|
|
25
|
+
memberQuerySchema,
|
|
26
|
+
createTierSchema,
|
|
27
|
+
updateTierSchema,
|
|
28
|
+
tierQuerySchema,
|
|
29
|
+
createNewsletterSchema,
|
|
30
|
+
updateNewsletterSchema,
|
|
31
|
+
newsletterQuerySchema,
|
|
32
|
+
createPageSchema,
|
|
33
|
+
updatePageSchema,
|
|
34
|
+
pageQuerySchema,
|
|
35
|
+
} from './schemas/index.js';
|
|
11
36
|
|
|
12
37
|
// Load environment variables
|
|
13
38
|
dotenv.config();
|
|
@@ -15,13 +40,17 @@ dotenv.config();
|
|
|
15
40
|
// Lazy-loaded modules (to avoid Node.js v25 Buffer compatibility issues at startup)
|
|
16
41
|
let ghostService = null;
|
|
17
42
|
let postService = null;
|
|
43
|
+
let pageService = null;
|
|
44
|
+
let newsletterService = null;
|
|
18
45
|
let imageProcessingService = null;
|
|
19
46
|
let urlValidator = null;
|
|
20
47
|
|
|
21
48
|
const loadServices = async () => {
|
|
22
49
|
if (!ghostService) {
|
|
23
|
-
ghostService = await import('./services/
|
|
50
|
+
ghostService = await import('./services/ghostServiceImproved.js');
|
|
24
51
|
postService = await import('./services/postService.js');
|
|
52
|
+
pageService = await import('./services/pageService.js');
|
|
53
|
+
newsletterService = await import('./services/newsletterService.js');
|
|
25
54
|
imageProcessingService = await import('./services/imageProcessingService.js');
|
|
26
55
|
urlValidator = await import('./utils/urlValidator.js');
|
|
27
56
|
}
|
|
@@ -51,26 +80,37 @@ const server = new McpServer({
|
|
|
51
80
|
|
|
52
81
|
// --- Register Tools ---
|
|
53
82
|
|
|
83
|
+
// --- Schema Definitions for Tools ---
|
|
84
|
+
const getTagsSchema = tagQuerySchema.partial();
|
|
85
|
+
const getTagSchema = z.object({
|
|
86
|
+
id: ghostIdSchema.optional().describe('The ID of the tag to retrieve.'),
|
|
87
|
+
slug: z.string().optional().describe('The slug of the tag to retrieve.'),
|
|
88
|
+
include: z.string().optional().describe('Additional resources to include (e.g., "count.posts").'),
|
|
89
|
+
});
|
|
90
|
+
const updateTagInputSchema = updateTagSchema.extend({ id: ghostIdSchema });
|
|
91
|
+
const deleteTagSchema = z.object({ id: ghostIdSchema });
|
|
92
|
+
|
|
54
93
|
// Get Tags Tool
|
|
55
94
|
server.tool(
|
|
56
95
|
'ghost_get_tags',
|
|
57
96
|
'Retrieves a list of tags from Ghost CMS. Can optionally filter by tag name.',
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
97
|
+
getTagsSchema,
|
|
98
|
+
async (rawInput) => {
|
|
99
|
+
const validation = validateToolInput(getTagsSchema, rawInput, 'ghost_get_tags');
|
|
100
|
+
if (!validation.success) {
|
|
101
|
+
return validation.errorResponse;
|
|
102
|
+
}
|
|
103
|
+
const input = validation.data;
|
|
104
|
+
|
|
65
105
|
console.error(`Executing tool: ghost_get_tags`);
|
|
66
106
|
try {
|
|
67
107
|
await loadServices();
|
|
68
108
|
const tags = await ghostService.getTags();
|
|
69
109
|
let result = tags;
|
|
70
110
|
|
|
71
|
-
if (name) {
|
|
72
|
-
result = tags.filter((tag) => tag.name.toLowerCase() === name.toLowerCase());
|
|
73
|
-
console.error(`Filtered tags by name "${name}". Found ${result.length} match(es).`);
|
|
111
|
+
if (input.name) {
|
|
112
|
+
result = tags.filter((tag) => tag.name.toLowerCase() === input.name.toLowerCase());
|
|
113
|
+
console.error(`Filtered tags by name "${input.name}". Found ${result.length} match(es).`);
|
|
74
114
|
} else {
|
|
75
115
|
console.error(`Retrieved ${tags.length} tags from Ghost.`);
|
|
76
116
|
}
|
|
@@ -80,6 +120,13 @@ server.tool(
|
|
|
80
120
|
};
|
|
81
121
|
} catch (error) {
|
|
82
122
|
console.error(`Error in ghost_get_tags:`, error);
|
|
123
|
+
if (error.name === 'ZodError') {
|
|
124
|
+
const validationError = ValidationError.fromZod(error, 'Tags retrieval');
|
|
125
|
+
return {
|
|
126
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
127
|
+
isError: true,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
83
130
|
return {
|
|
84
131
|
content: [{ type: 'text', text: `Error: ${error.message}` }],
|
|
85
132
|
isError: true,
|
|
@@ -92,21 +139,18 @@ server.tool(
|
|
|
92
139
|
server.tool(
|
|
93
140
|
'ghost_create_tag',
|
|
94
141
|
'Creates a new tag in Ghost CMS.',
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
},
|
|
105
|
-
async ({ name, description, slug }) => {
|
|
106
|
-
console.error(`Executing tool: ghost_create_tag with name: ${name}`);
|
|
142
|
+
createTagSchema,
|
|
143
|
+
async (rawInput) => {
|
|
144
|
+
const validation = validateToolInput(createTagSchema, rawInput, 'ghost_create_tag');
|
|
145
|
+
if (!validation.success) {
|
|
146
|
+
return validation.errorResponse;
|
|
147
|
+
}
|
|
148
|
+
const input = validation.data;
|
|
149
|
+
|
|
150
|
+
console.error(`Executing tool: ghost_create_tag with name: ${input.name}`);
|
|
107
151
|
try {
|
|
108
152
|
await loadServices();
|
|
109
|
-
const createdTag = await ghostService.createTag(
|
|
153
|
+
const createdTag = await ghostService.createTag(input);
|
|
110
154
|
console.error(`Tag created successfully. Tag ID: ${createdTag.id}`);
|
|
111
155
|
|
|
112
156
|
return {
|
|
@@ -114,6 +158,13 @@ server.tool(
|
|
|
114
158
|
};
|
|
115
159
|
} catch (error) {
|
|
116
160
|
console.error(`Error in ghost_create_tag:`, error);
|
|
161
|
+
if (error.name === 'ZodError') {
|
|
162
|
+
const validationError = ValidationError.fromZod(error, 'Tag creation');
|
|
163
|
+
return {
|
|
164
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
165
|
+
isError: true,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
117
168
|
return {
|
|
118
169
|
content: [{ type: 'text', text: `Error: ${error.message}` }],
|
|
119
170
|
isError: true,
|
|
@@ -126,15 +177,14 @@ server.tool(
|
|
|
126
177
|
server.tool(
|
|
127
178
|
'ghost_get_tag',
|
|
128
179
|
'Retrieves a single tag from Ghost CMS by ID or slug.',
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
async ({ id, slug, include }) => {
|
|
180
|
+
getTagSchema,
|
|
181
|
+
async (rawInput) => {
|
|
182
|
+
const validation = validateToolInput(getTagSchema, rawInput, 'ghost_get_tag');
|
|
183
|
+
if (!validation.success) {
|
|
184
|
+
return validation.errorResponse;
|
|
185
|
+
}
|
|
186
|
+
const { id, slug, include } = validation.data;
|
|
187
|
+
|
|
138
188
|
console.error(`Executing tool: ghost_get_tag`);
|
|
139
189
|
try {
|
|
140
190
|
if (!id && !slug) {
|
|
@@ -147,8 +197,7 @@ server.tool(
|
|
|
147
197
|
const identifier = slug ? `slug/${slug}` : id;
|
|
148
198
|
const options = include ? { include } : {};
|
|
149
199
|
|
|
150
|
-
const
|
|
151
|
-
const tag = await ghostServiceImproved.getTag(identifier, options);
|
|
200
|
+
const tag = await ghostService.getTag(identifier, options);
|
|
152
201
|
console.error(`Tag retrieved successfully. Tag ID: ${tag.id}`);
|
|
153
202
|
|
|
154
203
|
return {
|
|
@@ -156,6 +205,13 @@ server.tool(
|
|
|
156
205
|
};
|
|
157
206
|
} catch (error) {
|
|
158
207
|
console.error(`Error in ghost_get_tag:`, error);
|
|
208
|
+
if (error.name === 'ZodError') {
|
|
209
|
+
const validationError = ValidationError.fromZod(error, 'Tag retrieval');
|
|
210
|
+
return {
|
|
211
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
212
|
+
isError: true,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
159
215
|
return {
|
|
160
216
|
content: [{ type: 'text', text: `Error: ${error.message}` }],
|
|
161
217
|
isError: true,
|
|
@@ -168,35 +224,26 @@ server.tool(
|
|
|
168
224
|
server.tool(
|
|
169
225
|
'ghost_update_tag',
|
|
170
226
|
'Updates an existing tag in Ghost CMS.',
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
async ({ id, name, slug, description, feature_image, meta_title, meta_description }) => {
|
|
181
|
-
console.error(`Executing tool: ghost_update_tag for ID: ${id}`);
|
|
227
|
+
updateTagInputSchema,
|
|
228
|
+
async (rawInput) => {
|
|
229
|
+
const validation = validateToolInput(updateTagInputSchema, rawInput, 'ghost_update_tag');
|
|
230
|
+
if (!validation.success) {
|
|
231
|
+
return validation.errorResponse;
|
|
232
|
+
}
|
|
233
|
+
const input = validation.data;
|
|
234
|
+
|
|
235
|
+
console.error(`Executing tool: ghost_update_tag for ID: ${input.id}`);
|
|
182
236
|
try {
|
|
183
|
-
if (!id) {
|
|
237
|
+
if (!input.id) {
|
|
184
238
|
throw new Error('Tag ID is required');
|
|
185
239
|
}
|
|
186
240
|
|
|
187
241
|
await loadServices();
|
|
188
242
|
|
|
189
|
-
// Build update data object with only provided fields
|
|
190
|
-
const updateData =
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (description !== undefined) updateData.description = description;
|
|
194
|
-
if (feature_image !== undefined) updateData.feature_image = feature_image;
|
|
195
|
-
if (meta_title !== undefined) updateData.meta_title = meta_title;
|
|
196
|
-
if (meta_description !== undefined) updateData.meta_description = meta_description;
|
|
197
|
-
|
|
198
|
-
const ghostServiceImproved = await import('./services/ghostServiceImproved.js');
|
|
199
|
-
const updatedTag = await ghostServiceImproved.updateTag(id, updateData);
|
|
243
|
+
// Build update data object with only provided fields (exclude id from update data)
|
|
244
|
+
const { id, ...updateData } = input;
|
|
245
|
+
|
|
246
|
+
const updatedTag = await ghostService.updateTag(id, updateData);
|
|
200
247
|
console.error(`Tag updated successfully. Tag ID: ${updatedTag.id}`);
|
|
201
248
|
|
|
202
249
|
return {
|
|
@@ -204,6 +251,13 @@ server.tool(
|
|
|
204
251
|
};
|
|
205
252
|
} catch (error) {
|
|
206
253
|
console.error(`Error in ghost_update_tag:`, error);
|
|
254
|
+
if (error.name === 'ZodError') {
|
|
255
|
+
const validationError = ValidationError.fromZod(error, 'Tag update');
|
|
256
|
+
return {
|
|
257
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
258
|
+
isError: true,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
207
261
|
return {
|
|
208
262
|
content: [{ type: 'text', text: `Error: ${error.message}` }],
|
|
209
263
|
isError: true,
|
|
@@ -216,10 +270,14 @@ server.tool(
|
|
|
216
270
|
server.tool(
|
|
217
271
|
'ghost_delete_tag',
|
|
218
272
|
'Deletes a tag from Ghost CMS by ID. This operation is permanent.',
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
273
|
+
deleteTagSchema,
|
|
274
|
+
async (rawInput) => {
|
|
275
|
+
const validation = validateToolInput(deleteTagSchema, rawInput, 'ghost_delete_tag');
|
|
276
|
+
if (!validation.success) {
|
|
277
|
+
return validation.errorResponse;
|
|
278
|
+
}
|
|
279
|
+
const { id } = validation.data;
|
|
280
|
+
|
|
223
281
|
console.error(`Executing tool: ghost_delete_tag for ID: ${id}`);
|
|
224
282
|
try {
|
|
225
283
|
if (!id) {
|
|
@@ -228,8 +286,7 @@ server.tool(
|
|
|
228
286
|
|
|
229
287
|
await loadServices();
|
|
230
288
|
|
|
231
|
-
|
|
232
|
-
await ghostServiceImproved.deleteTag(id);
|
|
289
|
+
await ghostService.deleteTag(id);
|
|
233
290
|
console.error(`Tag deleted successfully. Tag ID: ${id}`);
|
|
234
291
|
|
|
235
292
|
return {
|
|
@@ -237,6 +294,13 @@ server.tool(
|
|
|
237
294
|
};
|
|
238
295
|
} catch (error) {
|
|
239
296
|
console.error(`Error in ghost_delete_tag:`, error);
|
|
297
|
+
if (error.name === 'ZodError') {
|
|
298
|
+
const validationError = ValidationError.fromZod(error, 'Tag deletion');
|
|
299
|
+
return {
|
|
300
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
301
|
+
isError: true,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
240
304
|
return {
|
|
241
305
|
content: [{ type: 'text', text: `Error: ${error.message}` }],
|
|
242
306
|
isError: true,
|
|
@@ -245,20 +309,27 @@ server.tool(
|
|
|
245
309
|
}
|
|
246
310
|
);
|
|
247
311
|
|
|
312
|
+
// --- Image Schema ---
|
|
313
|
+
const uploadImageSchema = z.object({
|
|
314
|
+
imageUrl: z.string().describe('The publicly accessible URL of the image to upload.'),
|
|
315
|
+
alt: z
|
|
316
|
+
.string()
|
|
317
|
+
.optional()
|
|
318
|
+
.describe('Alt text for the image. If omitted, a default will be generated from the filename.'),
|
|
319
|
+
});
|
|
320
|
+
|
|
248
321
|
// Upload Image Tool
|
|
249
322
|
server.tool(
|
|
250
323
|
'ghost_upload_image',
|
|
251
324
|
'Downloads an image from a URL, processes it, uploads it to Ghost CMS, and returns the final Ghost image URL and alt text.',
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
},
|
|
261
|
-
async ({ imageUrl, alt }) => {
|
|
325
|
+
uploadImageSchema,
|
|
326
|
+
async (rawInput) => {
|
|
327
|
+
const validation = validateToolInput(uploadImageSchema, rawInput, 'ghost_upload_image');
|
|
328
|
+
if (!validation.success) {
|
|
329
|
+
return validation.errorResponse;
|
|
330
|
+
}
|
|
331
|
+
const { imageUrl, alt } = validation.data;
|
|
332
|
+
|
|
262
333
|
console.error(`Executing tool: ghost_upload_image for URL: ${imageUrl}`);
|
|
263
334
|
let downloadedPath = null;
|
|
264
335
|
let processedPath = null;
|
|
@@ -288,10 +359,16 @@ server.tool(
|
|
|
288
359
|
writer.on('finish', resolve);
|
|
289
360
|
writer.on('error', reject);
|
|
290
361
|
});
|
|
362
|
+
// Track temp file for cleanup on process exit
|
|
363
|
+
trackTempFile(downloadedPath);
|
|
291
364
|
console.error(`Downloaded image to temporary path: ${downloadedPath}`);
|
|
292
365
|
|
|
293
366
|
// 3. Process the image
|
|
294
367
|
processedPath = await imageProcessingService.processImage(downloadedPath, tempDir);
|
|
368
|
+
// Track processed file for cleanup on process exit
|
|
369
|
+
if (processedPath !== downloadedPath) {
|
|
370
|
+
trackTempFile(processedPath);
|
|
371
|
+
}
|
|
295
372
|
console.error(`Processed image path: ${processedPath}`);
|
|
296
373
|
|
|
297
374
|
// 4. Determine Alt Text
|
|
@@ -319,63 +396,56 @@ server.tool(
|
|
|
319
396
|
isError: true,
|
|
320
397
|
};
|
|
321
398
|
} finally {
|
|
322
|
-
// Cleanup temporary files
|
|
323
|
-
|
|
324
|
-
fs.unlink(downloadedPath, (err) => {
|
|
325
|
-
if (err) console.error('Error deleting temporary downloaded file:', downloadedPath, err);
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
if (processedPath && processedPath !== downloadedPath) {
|
|
329
|
-
fs.unlink(processedPath, (err) => {
|
|
330
|
-
if (err) console.error('Error deleting temporary processed file:', processedPath, err);
|
|
331
|
-
});
|
|
332
|
-
}
|
|
399
|
+
// Cleanup temporary files with proper async/await
|
|
400
|
+
await cleanupTempFiles([downloadedPath, processedPath], console);
|
|
333
401
|
}
|
|
334
402
|
}
|
|
335
403
|
);
|
|
336
404
|
|
|
405
|
+
// --- Post Schema Definitions ---
|
|
406
|
+
const getPostsSchema = postQuerySchema.extend({
|
|
407
|
+
status: z
|
|
408
|
+
.enum(['published', 'draft', 'scheduled', 'all'])
|
|
409
|
+
.optional()
|
|
410
|
+
.describe('Filter posts by status. Options: published, draft, scheduled, all.'),
|
|
411
|
+
});
|
|
412
|
+
const getPostSchema = z.object({
|
|
413
|
+
id: ghostIdSchema.optional().describe('The ID of the post to retrieve.'),
|
|
414
|
+
slug: z.string().optional().describe('The slug of the post to retrieve.'),
|
|
415
|
+
include: z
|
|
416
|
+
.string()
|
|
417
|
+
.optional()
|
|
418
|
+
.describe('Comma-separated list of relations to include (e.g., "tags,authors").'),
|
|
419
|
+
});
|
|
420
|
+
const searchPostsSchema = z.object({
|
|
421
|
+
query: z.string().min(1).describe('Search query to find in post titles.'),
|
|
422
|
+
status: z
|
|
423
|
+
.enum(['published', 'draft', 'scheduled', 'all'])
|
|
424
|
+
.optional()
|
|
425
|
+
.describe('Filter by post status. Default searches all statuses.'),
|
|
426
|
+
limit: z
|
|
427
|
+
.number()
|
|
428
|
+
.int()
|
|
429
|
+
.min(1)
|
|
430
|
+
.max(50)
|
|
431
|
+
.optional()
|
|
432
|
+
.describe('Maximum number of results (1-50). Default is 15.'),
|
|
433
|
+
});
|
|
434
|
+
const updatePostInputSchema = updatePostSchema.extend({ id: ghostIdSchema });
|
|
435
|
+
const deletePostSchema = z.object({ id: ghostIdSchema });
|
|
436
|
+
|
|
337
437
|
// Create Post Tool
|
|
338
438
|
server.tool(
|
|
339
439
|
'ghost_create_post',
|
|
340
440
|
'Creates a new post in Ghost CMS.',
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
.
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
.array(z.string())
|
|
350
|
-
.optional()
|
|
351
|
-
.describe(
|
|
352
|
-
"List of tag names to associate with the post. Tags will be created if they don't exist."
|
|
353
|
-
),
|
|
354
|
-
published_at: z
|
|
355
|
-
.string()
|
|
356
|
-
.optional()
|
|
357
|
-
.describe("ISO 8601 date/time to publish the post. Required if status is 'scheduled'."),
|
|
358
|
-
custom_excerpt: z.string().optional().describe('A custom short summary for the post.'),
|
|
359
|
-
feature_image: z
|
|
360
|
-
.string()
|
|
361
|
-
.optional()
|
|
362
|
-
.describe(
|
|
363
|
-
'URL of the image (e.g., from ghost_upload_image tool) to use as the featured image.'
|
|
364
|
-
),
|
|
365
|
-
feature_image_alt: z.string().optional().describe('Alt text for the featured image.'),
|
|
366
|
-
feature_image_caption: z.string().optional().describe('Caption for the featured image.'),
|
|
367
|
-
meta_title: z
|
|
368
|
-
.string()
|
|
369
|
-
.optional()
|
|
370
|
-
.describe('Custom title for SEO (max 300 chars). Defaults to post title if omitted.'),
|
|
371
|
-
meta_description: z
|
|
372
|
-
.string()
|
|
373
|
-
.optional()
|
|
374
|
-
.describe(
|
|
375
|
-
'Custom description for SEO (max 500 chars). Defaults to excerpt or generated summary if omitted.'
|
|
376
|
-
),
|
|
377
|
-
},
|
|
378
|
-
async (input) => {
|
|
441
|
+
createPostSchema,
|
|
442
|
+
async (rawInput) => {
|
|
443
|
+
const validation = validateToolInput(createPostSchema, rawInput, 'ghost_create_post');
|
|
444
|
+
if (!validation.success) {
|
|
445
|
+
return validation.errorResponse;
|
|
446
|
+
}
|
|
447
|
+
const input = validation.data;
|
|
448
|
+
|
|
379
449
|
console.error(`Executing tool: ghost_create_post with title: ${input.title}`);
|
|
380
450
|
try {
|
|
381
451
|
await loadServices();
|
|
@@ -387,6 +457,13 @@ server.tool(
|
|
|
387
457
|
};
|
|
388
458
|
} catch (error) {
|
|
389
459
|
console.error(`Error in ghost_create_post:`, error);
|
|
460
|
+
if (error.name === 'ZodError') {
|
|
461
|
+
const validationError = ValidationError.fromZod(error, 'Post creation');
|
|
462
|
+
return {
|
|
463
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
464
|
+
isError: true,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
390
467
|
return {
|
|
391
468
|
content: [{ type: 'text', text: `Error creating post: ${error.message}` }],
|
|
392
469
|
isError: true,
|
|
@@ -399,32 +476,14 @@ server.tool(
|
|
|
399
476
|
server.tool(
|
|
400
477
|
'ghost_get_posts',
|
|
401
478
|
'Retrieves a list of posts from Ghost CMS with pagination, filtering, and sorting options.',
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
.
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
status: z
|
|
411
|
-
.enum(['published', 'draft', 'scheduled', 'all'])
|
|
412
|
-
.optional()
|
|
413
|
-
.describe('Filter posts by status. Options: published, draft, scheduled, all.'),
|
|
414
|
-
include: z
|
|
415
|
-
.string()
|
|
416
|
-
.optional()
|
|
417
|
-
.describe('Comma-separated list of relations to include (e.g., "tags,authors").'),
|
|
418
|
-
filter: z
|
|
419
|
-
.string()
|
|
420
|
-
.optional()
|
|
421
|
-
.describe('Ghost NQL filter string for advanced filtering (e.g., "featured:true").'),
|
|
422
|
-
order: z
|
|
423
|
-
.string()
|
|
424
|
-
.optional()
|
|
425
|
-
.describe('Sort order for results (e.g., "published_at DESC", "title ASC").'),
|
|
426
|
-
},
|
|
427
|
-
async (input) => {
|
|
479
|
+
getPostsSchema,
|
|
480
|
+
async (rawInput) => {
|
|
481
|
+
const validation = validateToolInput(getPostsSchema, rawInput, 'ghost_get_posts');
|
|
482
|
+
if (!validation.success) {
|
|
483
|
+
return validation.errorResponse;
|
|
484
|
+
}
|
|
485
|
+
const input = validation.data;
|
|
486
|
+
|
|
428
487
|
console.error(`Executing tool: ghost_get_posts`);
|
|
429
488
|
try {
|
|
430
489
|
await loadServices();
|
|
@@ -446,6 +505,13 @@ server.tool(
|
|
|
446
505
|
};
|
|
447
506
|
} catch (error) {
|
|
448
507
|
console.error(`Error in ghost_get_posts:`, error);
|
|
508
|
+
if (error.name === 'ZodError') {
|
|
509
|
+
const validationError = ValidationError.fromZod(error, 'Posts retrieval');
|
|
510
|
+
return {
|
|
511
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
512
|
+
isError: true,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
449
515
|
return {
|
|
450
516
|
content: [{ type: 'text', text: `Error retrieving posts: ${error.message}` }],
|
|
451
517
|
isError: true,
|
|
@@ -458,15 +524,14 @@ server.tool(
|
|
|
458
524
|
server.tool(
|
|
459
525
|
'ghost_get_post',
|
|
460
526
|
'Retrieves a single post from Ghost CMS by ID or slug.',
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
async (input) => {
|
|
527
|
+
getPostSchema,
|
|
528
|
+
async (rawInput) => {
|
|
529
|
+
const validation = validateToolInput(getPostSchema, rawInput, 'ghost_get_post');
|
|
530
|
+
if (!validation.success) {
|
|
531
|
+
return validation.errorResponse;
|
|
532
|
+
}
|
|
533
|
+
const input = validation.data;
|
|
534
|
+
|
|
470
535
|
console.error(`Executing tool: ghost_get_post`);
|
|
471
536
|
try {
|
|
472
537
|
// Validate that at least one of id or slug is provided
|
|
@@ -491,6 +556,13 @@ server.tool(
|
|
|
491
556
|
};
|
|
492
557
|
} catch (error) {
|
|
493
558
|
console.error(`Error in ghost_get_post:`, error);
|
|
559
|
+
if (error.name === 'ZodError') {
|
|
560
|
+
const validationError = ValidationError.fromZod(error, 'Post retrieval');
|
|
561
|
+
return {
|
|
562
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
563
|
+
isError: true,
|
|
564
|
+
};
|
|
565
|
+
}
|
|
494
566
|
return {
|
|
495
567
|
content: [{ type: 'text', text: `Error retrieving post: ${error.message}` }],
|
|
496
568
|
isError: true,
|
|
@@ -503,20 +575,14 @@ server.tool(
|
|
|
503
575
|
server.tool(
|
|
504
576
|
'ghost_search_posts',
|
|
505
577
|
'Search for posts in Ghost CMS by query string with optional status filtering.',
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
.
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
.min(1)
|
|
515
|
-
.max(50)
|
|
516
|
-
.optional()
|
|
517
|
-
.describe('Maximum number of results (1-50). Default is 15.'),
|
|
518
|
-
},
|
|
519
|
-
async (input) => {
|
|
578
|
+
searchPostsSchema,
|
|
579
|
+
async (rawInput) => {
|
|
580
|
+
const validation = validateToolInput(searchPostsSchema, rawInput, 'ghost_search_posts');
|
|
581
|
+
if (!validation.success) {
|
|
582
|
+
return validation.errorResponse;
|
|
583
|
+
}
|
|
584
|
+
const input = validation.data;
|
|
585
|
+
|
|
520
586
|
console.error(`Executing tool: ghost_search_posts with query: ${input.query}`);
|
|
521
587
|
try {
|
|
522
588
|
await loadServices();
|
|
@@ -526,9 +592,7 @@ server.tool(
|
|
|
526
592
|
if (input.status !== undefined) options.status = input.status;
|
|
527
593
|
if (input.limit !== undefined) options.limit = input.limit;
|
|
528
594
|
|
|
529
|
-
|
|
530
|
-
const ghostServiceImproved = await import('./services/ghostServiceImproved.js');
|
|
531
|
-
const posts = await ghostServiceImproved.searchPosts(input.query, options);
|
|
595
|
+
const posts = await ghostService.searchPosts(input.query, options);
|
|
532
596
|
console.error(`Found ${posts.length} posts matching "${input.query}".`);
|
|
533
597
|
|
|
534
598
|
return {
|
|
@@ -536,6 +600,13 @@ server.tool(
|
|
|
536
600
|
};
|
|
537
601
|
} catch (error) {
|
|
538
602
|
console.error(`Error in ghost_search_posts:`, error);
|
|
603
|
+
if (error.name === 'ZodError') {
|
|
604
|
+
const validationError = ValidationError.fromZod(error, 'Post search');
|
|
605
|
+
return {
|
|
606
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
607
|
+
isError: true,
|
|
608
|
+
};
|
|
609
|
+
}
|
|
539
610
|
return {
|
|
540
611
|
content: [{ type: 'text', text: `Error searching posts: ${error.message}` }],
|
|
541
612
|
isError: true,
|
|
@@ -548,35 +619,14 @@ server.tool(
|
|
|
548
619
|
server.tool(
|
|
549
620
|
'ghost_update_post',
|
|
550
621
|
'Updates an existing post in Ghost CMS. Can update title, content, status, tags, images, and SEO fields.',
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
tags: z
|
|
560
|
-
.array(z.string())
|
|
561
|
-
.optional()
|
|
562
|
-
.describe('New list of tag names to associate with the post.'),
|
|
563
|
-
feature_image: z.string().optional().describe('New featured image URL.'),
|
|
564
|
-
feature_image_alt: z.string().optional().describe('New alt text for the featured image.'),
|
|
565
|
-
feature_image_caption: z.string().optional().describe('New caption for the featured image.'),
|
|
566
|
-
meta_title: z.string().optional().describe('New custom title for SEO (max 300 chars).'),
|
|
567
|
-
meta_description: z
|
|
568
|
-
.string()
|
|
569
|
-
.optional()
|
|
570
|
-
.describe('New custom description for SEO (max 500 chars).'),
|
|
571
|
-
published_at: z
|
|
572
|
-
.string()
|
|
573
|
-
.optional()
|
|
574
|
-
.describe(
|
|
575
|
-
"New publication date/time in ISO 8601 format. Required if changing status to 'scheduled'."
|
|
576
|
-
),
|
|
577
|
-
custom_excerpt: z.string().optional().describe('New custom short summary for the post.'),
|
|
578
|
-
},
|
|
579
|
-
async (input) => {
|
|
622
|
+
updatePostInputSchema,
|
|
623
|
+
async (rawInput) => {
|
|
624
|
+
const validation = validateToolInput(updatePostInputSchema, rawInput, 'ghost_update_post');
|
|
625
|
+
if (!validation.success) {
|
|
626
|
+
return validation.errorResponse;
|
|
627
|
+
}
|
|
628
|
+
const input = validation.data;
|
|
629
|
+
|
|
580
630
|
console.error(`Executing tool: ghost_update_post for post ID: ${input.id}`);
|
|
581
631
|
try {
|
|
582
632
|
await loadServices();
|
|
@@ -584,9 +634,7 @@ server.tool(
|
|
|
584
634
|
// Extract ID from input and build update data
|
|
585
635
|
const { id, ...updateData } = input;
|
|
586
636
|
|
|
587
|
-
|
|
588
|
-
const ghostServiceImproved = await import('./services/ghostServiceImproved.js');
|
|
589
|
-
const updatedPost = await ghostServiceImproved.updatePost(id, updateData);
|
|
637
|
+
const updatedPost = await ghostService.updatePost(id, updateData);
|
|
590
638
|
console.error(`Post updated successfully. Post ID: ${updatedPost.id}`);
|
|
591
639
|
|
|
592
640
|
return {
|
|
@@ -594,6 +642,13 @@ server.tool(
|
|
|
594
642
|
};
|
|
595
643
|
} catch (error) {
|
|
596
644
|
console.error(`Error in ghost_update_post:`, error);
|
|
645
|
+
if (error.name === 'ZodError') {
|
|
646
|
+
const validationError = ValidationError.fromZod(error, 'Post update');
|
|
647
|
+
return {
|
|
648
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
649
|
+
isError: true,
|
|
650
|
+
};
|
|
651
|
+
}
|
|
597
652
|
return {
|
|
598
653
|
content: [{ type: 'text', text: `Error updating post: ${error.message}` }],
|
|
599
654
|
isError: true,
|
|
@@ -606,17 +661,19 @@ server.tool(
|
|
|
606
661
|
server.tool(
|
|
607
662
|
'ghost_delete_post',
|
|
608
663
|
'Deletes a post from Ghost CMS by ID. This operation is permanent and cannot be undone.',
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
664
|
+
deletePostSchema,
|
|
665
|
+
async (rawInput) => {
|
|
666
|
+
const validation = validateToolInput(deletePostSchema, rawInput, 'ghost_delete_post');
|
|
667
|
+
if (!validation.success) {
|
|
668
|
+
return validation.errorResponse;
|
|
669
|
+
}
|
|
670
|
+
const { id } = validation.data;
|
|
671
|
+
|
|
613
672
|
console.error(`Executing tool: ghost_delete_post for post ID: ${id}`);
|
|
614
673
|
try {
|
|
615
674
|
await loadServices();
|
|
616
675
|
|
|
617
|
-
|
|
618
|
-
const ghostServiceImproved = await import('./services/ghostServiceImproved.js');
|
|
619
|
-
await ghostServiceImproved.deletePost(id);
|
|
676
|
+
await ghostService.deletePost(id);
|
|
620
677
|
console.error(`Post deleted successfully. Post ID: ${id}`);
|
|
621
678
|
|
|
622
679
|
return {
|
|
@@ -624,6 +681,13 @@ server.tool(
|
|
|
624
681
|
};
|
|
625
682
|
} catch (error) {
|
|
626
683
|
console.error(`Error in ghost_delete_post:`, error);
|
|
684
|
+
if (error.name === 'ZodError') {
|
|
685
|
+
const validationError = ValidationError.fromZod(error, 'Post deletion');
|
|
686
|
+
return {
|
|
687
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
688
|
+
isError: true,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
627
691
|
return {
|
|
628
692
|
content: [{ type: 'text', text: `Error deleting post: ${error.message}` }],
|
|
629
693
|
isError: true,
|
|
@@ -637,30 +701,54 @@ server.tool(
|
|
|
637
701
|
// Pages are similar to posts but do NOT support tags
|
|
638
702
|
// =============================================================================
|
|
639
703
|
|
|
640
|
-
//
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
limit: z
|
|
646
|
-
.number()
|
|
647
|
-
.min(1)
|
|
648
|
-
.max(100)
|
|
649
|
-
.optional()
|
|
650
|
-
.describe('Number of pages to retrieve (1-100). Default is 15.'),
|
|
651
|
-
page: z.number().min(1).optional().describe('Page number for pagination. Default is 1.'),
|
|
652
|
-
status: z
|
|
653
|
-
.enum(['published', 'draft', 'scheduled', 'all'])
|
|
654
|
-
.optional()
|
|
655
|
-
.describe('Filter pages by status.'),
|
|
704
|
+
// --- Page Schema Definitions ---
|
|
705
|
+
const getPageSchema = z
|
|
706
|
+
.object({
|
|
707
|
+
id: ghostIdSchema.optional().describe('The ID of the page to retrieve.'),
|
|
708
|
+
slug: z.string().optional().describe('The slug of the page to retrieve.'),
|
|
656
709
|
include: z
|
|
657
710
|
.string()
|
|
658
711
|
.optional()
|
|
659
712
|
.describe('Comma-separated list of relations to include (e.g., "authors").'),
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
713
|
+
})
|
|
714
|
+
.refine((data) => data.id || data.slug, {
|
|
715
|
+
message: 'Either id or slug is required to retrieve a page',
|
|
716
|
+
});
|
|
717
|
+
const updatePageInputSchema = z
|
|
718
|
+
.object({ id: ghostIdSchema.describe('The ID of the page to update.') })
|
|
719
|
+
.merge(updatePageSchema);
|
|
720
|
+
const deletePageSchema = z.object({ id: ghostIdSchema.describe('The ID of the page to delete.') });
|
|
721
|
+
const searchPagesSchema = z.object({
|
|
722
|
+
query: z
|
|
723
|
+
.string()
|
|
724
|
+
.min(1, 'Search query cannot be empty')
|
|
725
|
+
.describe('Search query to find in page titles.'),
|
|
726
|
+
status: z
|
|
727
|
+
.enum(['published', 'draft', 'scheduled', 'all'])
|
|
728
|
+
.optional()
|
|
729
|
+
.describe('Filter by page status. Default searches all statuses.'),
|
|
730
|
+
limit: z
|
|
731
|
+
.number()
|
|
732
|
+
.int()
|
|
733
|
+
.min(1)
|
|
734
|
+
.max(50)
|
|
735
|
+
.default(15)
|
|
736
|
+
.optional()
|
|
737
|
+
.describe('Maximum number of results (1-50). Default is 15.'),
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
// Get Pages Tool
|
|
741
|
+
server.tool(
|
|
742
|
+
'ghost_get_pages',
|
|
743
|
+
'Retrieves a list of pages from Ghost CMS with pagination, filtering, and sorting options.',
|
|
744
|
+
pageQuerySchema,
|
|
745
|
+
async (rawInput) => {
|
|
746
|
+
const validation = validateToolInput(pageQuerySchema, rawInput, 'ghost_get_pages');
|
|
747
|
+
if (!validation.success) {
|
|
748
|
+
return validation.errorResponse;
|
|
749
|
+
}
|
|
750
|
+
const input = validation.data;
|
|
751
|
+
|
|
664
752
|
console.error(`Executing tool: ghost_get_pages`);
|
|
665
753
|
try {
|
|
666
754
|
await loadServices();
|
|
@@ -668,13 +756,13 @@ server.tool(
|
|
|
668
756
|
const options = {};
|
|
669
757
|
if (input.limit !== undefined) options.limit = input.limit;
|
|
670
758
|
if (input.page !== undefined) options.page = input.page;
|
|
671
|
-
if (input.status !== undefined) options.status = input.status;
|
|
672
|
-
if (input.include !== undefined) options.include = input.include;
|
|
673
759
|
if (input.filter !== undefined) options.filter = input.filter;
|
|
760
|
+
if (input.include !== undefined) options.include = input.include;
|
|
761
|
+
if (input.fields !== undefined) options.fields = input.fields;
|
|
762
|
+
if (input.formats !== undefined) options.formats = input.formats;
|
|
674
763
|
if (input.order !== undefined) options.order = input.order;
|
|
675
764
|
|
|
676
|
-
const
|
|
677
|
-
const pages = await ghostServiceImproved.getPages(options);
|
|
765
|
+
const pages = await ghostService.getPages(options);
|
|
678
766
|
console.error(`Retrieved ${pages.length} pages from Ghost.`);
|
|
679
767
|
|
|
680
768
|
return {
|
|
@@ -682,6 +770,13 @@ server.tool(
|
|
|
682
770
|
};
|
|
683
771
|
} catch (error) {
|
|
684
772
|
console.error(`Error in ghost_get_pages:`, error);
|
|
773
|
+
if (error.name === 'ZodError') {
|
|
774
|
+
const validationError = ValidationError.fromZod(error, 'Page query');
|
|
775
|
+
return {
|
|
776
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
777
|
+
isError: true,
|
|
778
|
+
};
|
|
779
|
+
}
|
|
685
780
|
return {
|
|
686
781
|
content: [{ type: 'text', text: `Error retrieving pages: ${error.message}` }],
|
|
687
782
|
isError: true,
|
|
@@ -694,21 +789,16 @@ server.tool(
|
|
|
694
789
|
server.tool(
|
|
695
790
|
'ghost_get_page',
|
|
696
791
|
'Retrieves a single page from Ghost CMS by ID or slug.',
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
.
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
async (input) => {
|
|
792
|
+
getPageSchema,
|
|
793
|
+
async (rawInput) => {
|
|
794
|
+
const validation = validateToolInput(getPageSchema, rawInput, 'ghost_get_page');
|
|
795
|
+
if (!validation.success) {
|
|
796
|
+
return validation.errorResponse;
|
|
797
|
+
}
|
|
798
|
+
const input = validation.data;
|
|
799
|
+
|
|
706
800
|
console.error(`Executing tool: ghost_get_page`);
|
|
707
801
|
try {
|
|
708
|
-
if (!input.id && !input.slug) {
|
|
709
|
-
throw new Error('Either id or slug is required to retrieve a page');
|
|
710
|
-
}
|
|
711
|
-
|
|
712
802
|
await loadServices();
|
|
713
803
|
|
|
714
804
|
const options = {};
|
|
@@ -716,8 +806,7 @@ server.tool(
|
|
|
716
806
|
|
|
717
807
|
const identifier = input.id || `slug/${input.slug}`;
|
|
718
808
|
|
|
719
|
-
const
|
|
720
|
-
const page = await ghostServiceImproved.getPage(identifier, options);
|
|
809
|
+
const page = await ghostService.getPage(identifier, options);
|
|
721
810
|
console.error(`Retrieved page: ${page.title} (ID: ${page.id})`);
|
|
722
811
|
|
|
723
812
|
return {
|
|
@@ -725,6 +814,13 @@ server.tool(
|
|
|
725
814
|
};
|
|
726
815
|
} catch (error) {
|
|
727
816
|
console.error(`Error in ghost_get_page:`, error);
|
|
817
|
+
if (error.name === 'ZodError') {
|
|
818
|
+
const validationError = ValidationError.fromZod(error, 'Get page');
|
|
819
|
+
return {
|
|
820
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
821
|
+
isError: true,
|
|
822
|
+
};
|
|
823
|
+
}
|
|
728
824
|
return {
|
|
729
825
|
content: [{ type: 'text', text: `Error retrieving page: ${error.message}` }],
|
|
730
826
|
isError: true,
|
|
@@ -736,40 +832,19 @@ server.tool(
|
|
|
736
832
|
// Create Page Tool
|
|
737
833
|
server.tool(
|
|
738
834
|
'ghost_create_page',
|
|
739
|
-
'Creates a new page in Ghost CMS. Note: Pages do NOT
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
.
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
published_at: z
|
|
749
|
-
.string()
|
|
750
|
-
.optional()
|
|
751
|
-
.describe("ISO 8601 date/time to publish the page. Required if status is 'scheduled'."),
|
|
752
|
-
custom_excerpt: z.string().optional().describe('A custom short summary for the page.'),
|
|
753
|
-
feature_image: z.string().optional().describe('URL of the image to use as the featured image.'),
|
|
754
|
-
feature_image_alt: z.string().optional().describe('Alt text for the featured image.'),
|
|
755
|
-
feature_image_caption: z.string().optional().describe('Caption for the featured image.'),
|
|
756
|
-
meta_title: z
|
|
757
|
-
.string()
|
|
758
|
-
.optional()
|
|
759
|
-
.describe('Custom title for SEO (max 70 chars). Defaults to page title if omitted.'),
|
|
760
|
-
meta_description: z
|
|
761
|
-
.string()
|
|
762
|
-
.optional()
|
|
763
|
-
.describe(
|
|
764
|
-
'Custom description for SEO (max 160 chars). Defaults to excerpt or generated summary if omitted.'
|
|
765
|
-
),
|
|
766
|
-
},
|
|
767
|
-
async (input) => {
|
|
835
|
+
'Creates a new page in Ghost CMS. Note: Pages do NOT typically use tags (unlike posts).',
|
|
836
|
+
createPageSchema,
|
|
837
|
+
async (rawInput) => {
|
|
838
|
+
const validation = validateToolInput(createPageSchema, rawInput, 'ghost_create_page');
|
|
839
|
+
if (!validation.success) {
|
|
840
|
+
return validation.errorResponse;
|
|
841
|
+
}
|
|
842
|
+
const input = validation.data;
|
|
843
|
+
|
|
768
844
|
console.error(`Executing tool: ghost_create_page with title: ${input.title}`);
|
|
769
845
|
try {
|
|
770
846
|
await loadServices();
|
|
771
847
|
|
|
772
|
-
const pageService = await import('./services/pageService.js');
|
|
773
848
|
const createdPage = await pageService.createPageService(input);
|
|
774
849
|
console.error(`Page created successfully. Page ID: ${createdPage.id}`);
|
|
775
850
|
|
|
@@ -778,6 +853,13 @@ server.tool(
|
|
|
778
853
|
};
|
|
779
854
|
} catch (error) {
|
|
780
855
|
console.error(`Error in ghost_create_page:`, error);
|
|
856
|
+
if (error.name === 'ZodError') {
|
|
857
|
+
const validationError = ValidationError.fromZod(error, 'Page creation');
|
|
858
|
+
return {
|
|
859
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
860
|
+
isError: true,
|
|
861
|
+
};
|
|
862
|
+
}
|
|
781
863
|
return {
|
|
782
864
|
content: [{ type: 'text', text: `Error creating page: ${error.message}` }],
|
|
783
865
|
isError: true,
|
|
@@ -789,33 +871,22 @@ server.tool(
|
|
|
789
871
|
// Update Page Tool
|
|
790
872
|
server.tool(
|
|
791
873
|
'ghost_update_page',
|
|
792
|
-
'Updates an existing page in Ghost CMS. Can update title, content, status, images, and SEO fields.
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
// NO tags parameter - pages don't support tags
|
|
802
|
-
feature_image: z.string().optional().describe('New featured image URL.'),
|
|
803
|
-
feature_image_alt: z.string().optional().describe('New alt text for the featured image.'),
|
|
804
|
-
feature_image_caption: z.string().optional().describe('New caption for the featured image.'),
|
|
805
|
-
meta_title: z.string().optional().describe('New custom title for SEO.'),
|
|
806
|
-
meta_description: z.string().optional().describe('New custom description for SEO.'),
|
|
807
|
-
published_at: z.string().optional().describe('New publication date/time in ISO 8601 format.'),
|
|
808
|
-
custom_excerpt: z.string().optional().describe('New custom short summary for the page.'),
|
|
809
|
-
},
|
|
810
|
-
async (input) => {
|
|
874
|
+
'Updates an existing page in Ghost CMS. Can update title, content, status, images, and SEO fields.',
|
|
875
|
+
updatePageInputSchema,
|
|
876
|
+
async (rawInput) => {
|
|
877
|
+
const validation = validateToolInput(updatePageInputSchema, rawInput, 'ghost_update_page');
|
|
878
|
+
if (!validation.success) {
|
|
879
|
+
return validation.errorResponse;
|
|
880
|
+
}
|
|
881
|
+
const input = validation.data;
|
|
882
|
+
|
|
811
883
|
console.error(`Executing tool: ghost_update_page for page ID: ${input.id}`);
|
|
812
884
|
try {
|
|
813
885
|
await loadServices();
|
|
814
886
|
|
|
815
887
|
const { id, ...updateData } = input;
|
|
816
888
|
|
|
817
|
-
const
|
|
818
|
-
const updatedPage = await ghostServiceImproved.updatePage(id, updateData);
|
|
889
|
+
const updatedPage = await ghostService.updatePage(id, updateData);
|
|
819
890
|
console.error(`Page updated successfully. Page ID: ${updatedPage.id}`);
|
|
820
891
|
|
|
821
892
|
return {
|
|
@@ -823,6 +894,13 @@ server.tool(
|
|
|
823
894
|
};
|
|
824
895
|
} catch (error) {
|
|
825
896
|
console.error(`Error in ghost_update_page:`, error);
|
|
897
|
+
if (error.name === 'ZodError') {
|
|
898
|
+
const validationError = ValidationError.fromZod(error, 'Page update');
|
|
899
|
+
return {
|
|
900
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
901
|
+
isError: true,
|
|
902
|
+
};
|
|
903
|
+
}
|
|
826
904
|
return {
|
|
827
905
|
content: [{ type: 'text', text: `Error updating page: ${error.message}` }],
|
|
828
906
|
isError: true,
|
|
@@ -835,16 +913,19 @@ server.tool(
|
|
|
835
913
|
server.tool(
|
|
836
914
|
'ghost_delete_page',
|
|
837
915
|
'Deletes a page from Ghost CMS by ID. This operation is permanent and cannot be undone.',
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
916
|
+
deletePageSchema,
|
|
917
|
+
async (rawInput) => {
|
|
918
|
+
const validation = validateToolInput(deletePageSchema, rawInput, 'ghost_delete_page');
|
|
919
|
+
if (!validation.success) {
|
|
920
|
+
return validation.errorResponse;
|
|
921
|
+
}
|
|
922
|
+
const { id } = validation.data;
|
|
923
|
+
|
|
842
924
|
console.error(`Executing tool: ghost_delete_page for page ID: ${id}`);
|
|
843
925
|
try {
|
|
844
926
|
await loadServices();
|
|
845
927
|
|
|
846
|
-
|
|
847
|
-
await ghostServiceImproved.deletePage(id);
|
|
928
|
+
await ghostService.deletePage(id);
|
|
848
929
|
console.error(`Page deleted successfully. Page ID: ${id}`);
|
|
849
930
|
|
|
850
931
|
return {
|
|
@@ -852,6 +933,13 @@ server.tool(
|
|
|
852
933
|
};
|
|
853
934
|
} catch (error) {
|
|
854
935
|
console.error(`Error in ghost_delete_page:`, error);
|
|
936
|
+
if (error.name === 'ZodError') {
|
|
937
|
+
const validationError = ValidationError.fromZod(error, 'Page deletion');
|
|
938
|
+
return {
|
|
939
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
940
|
+
isError: true,
|
|
941
|
+
};
|
|
942
|
+
}
|
|
855
943
|
return {
|
|
856
944
|
content: [{ type: 'text', text: `Error deleting page: ${error.message}` }],
|
|
857
945
|
isError: true,
|
|
@@ -864,20 +952,14 @@ server.tool(
|
|
|
864
952
|
server.tool(
|
|
865
953
|
'ghost_search_pages',
|
|
866
954
|
'Search for pages in Ghost CMS by query string with optional status filtering.',
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
.
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
.min(1)
|
|
876
|
-
.max(50)
|
|
877
|
-
.optional()
|
|
878
|
-
.describe('Maximum number of results (1-50). Default is 15.'),
|
|
879
|
-
},
|
|
880
|
-
async (input) => {
|
|
955
|
+
searchPagesSchema,
|
|
956
|
+
async (rawInput) => {
|
|
957
|
+
const validation = validateToolInput(searchPagesSchema, rawInput, 'ghost_search_pages');
|
|
958
|
+
if (!validation.success) {
|
|
959
|
+
return validation.errorResponse;
|
|
960
|
+
}
|
|
961
|
+
const input = validation.data;
|
|
962
|
+
|
|
881
963
|
console.error(`Executing tool: ghost_search_pages with query: ${input.query}`);
|
|
882
964
|
try {
|
|
883
965
|
await loadServices();
|
|
@@ -886,8 +968,7 @@ server.tool(
|
|
|
886
968
|
if (input.status !== undefined) options.status = input.status;
|
|
887
969
|
if (input.limit !== undefined) options.limit = input.limit;
|
|
888
970
|
|
|
889
|
-
const
|
|
890
|
-
const pages = await ghostServiceImproved.searchPages(input.query, options);
|
|
971
|
+
const pages = await ghostService.searchPages(input.query, options);
|
|
891
972
|
console.error(`Found ${pages.length} pages matching "${input.query}".`);
|
|
892
973
|
|
|
893
974
|
return {
|
|
@@ -895,6 +976,13 @@ server.tool(
|
|
|
895
976
|
};
|
|
896
977
|
} catch (error) {
|
|
897
978
|
console.error(`Error in ghost_search_pages:`, error);
|
|
979
|
+
if (error.name === 'ZodError') {
|
|
980
|
+
const validationError = ValidationError.fromZod(error, 'Page search');
|
|
981
|
+
return {
|
|
982
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
983
|
+
isError: true,
|
|
984
|
+
};
|
|
985
|
+
}
|
|
898
986
|
return {
|
|
899
987
|
content: [{ type: 'text', text: `Error searching pages: ${error.message}` }],
|
|
900
988
|
isError: true,
|
|
@@ -908,31 +996,46 @@ server.tool(
|
|
|
908
996
|
// Member management for Ghost CMS subscribers
|
|
909
997
|
// =============================================================================
|
|
910
998
|
|
|
999
|
+
// --- Member Schema Definitions ---
|
|
1000
|
+
const updateMemberInputSchema = z.object({ id: ghostIdSchema }).merge(updateMemberSchema);
|
|
1001
|
+
const deleteMemberSchema = z.object({ id: ghostIdSchema });
|
|
1002
|
+
const getMembersSchema = memberQuerySchema.omit({ search: true });
|
|
1003
|
+
const getMemberSchema = z
|
|
1004
|
+
.object({
|
|
1005
|
+
id: ghostIdSchema.optional().describe('The ID of the member to retrieve.'),
|
|
1006
|
+
email: emailSchema.optional().describe('The email of the member to retrieve.'),
|
|
1007
|
+
})
|
|
1008
|
+
.refine((data) => data.id || data.email, {
|
|
1009
|
+
message: 'Either id or email must be provided',
|
|
1010
|
+
});
|
|
1011
|
+
const searchMembersSchema = z.object({
|
|
1012
|
+
query: z.string().min(1).describe('Search query to match against member name or email.'),
|
|
1013
|
+
limit: z
|
|
1014
|
+
.number()
|
|
1015
|
+
.int()
|
|
1016
|
+
.min(1)
|
|
1017
|
+
.max(50)
|
|
1018
|
+
.optional()
|
|
1019
|
+
.describe('Maximum number of results to return (1-50). Default is 15.'),
|
|
1020
|
+
});
|
|
1021
|
+
|
|
911
1022
|
// Create Member Tool
|
|
912
1023
|
server.tool(
|
|
913
1024
|
'ghost_create_member',
|
|
914
1025
|
'Creates a new member (subscriber) in Ghost CMS.',
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
.describe('List of newsletter objects with id field to subscribe the member to.'),
|
|
924
|
-
subscribed: z
|
|
925
|
-
.boolean()
|
|
926
|
-
.optional()
|
|
927
|
-
.describe('Whether the member is subscribed to emails. Defaults to true.'),
|
|
928
|
-
},
|
|
929
|
-
async (input) => {
|
|
1026
|
+
createMemberSchema,
|
|
1027
|
+
async (rawInput) => {
|
|
1028
|
+
const validation = validateToolInput(createMemberSchema, rawInput, 'ghost_create_member');
|
|
1029
|
+
if (!validation.success) {
|
|
1030
|
+
return validation.errorResponse;
|
|
1031
|
+
}
|
|
1032
|
+
const input = validation.data;
|
|
1033
|
+
|
|
930
1034
|
console.error(`Executing tool: ghost_create_member with email: ${input.email}`);
|
|
931
1035
|
try {
|
|
932
1036
|
await loadServices();
|
|
933
1037
|
|
|
934
|
-
const
|
|
935
|
-
const createdMember = await ghostServiceImproved.createMember(input);
|
|
1038
|
+
const createdMember = await ghostService.createMember(input);
|
|
936
1039
|
console.error(`Member created successfully. Member ID: ${createdMember.id}`);
|
|
937
1040
|
|
|
938
1041
|
return {
|
|
@@ -940,6 +1043,13 @@ server.tool(
|
|
|
940
1043
|
};
|
|
941
1044
|
} catch (error) {
|
|
942
1045
|
console.error(`Error in ghost_create_member:`, error);
|
|
1046
|
+
if (error.name === 'ZodError') {
|
|
1047
|
+
const validationError = ValidationError.fromZod(error, 'Member creation');
|
|
1048
|
+
return {
|
|
1049
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1050
|
+
isError: true,
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
943
1053
|
return {
|
|
944
1054
|
content: [{ type: 'text', text: `Error creating member: ${error.message}` }],
|
|
945
1055
|
isError: true,
|
|
@@ -952,29 +1062,21 @@ server.tool(
|
|
|
952
1062
|
server.tool(
|
|
953
1063
|
'ghost_update_member',
|
|
954
1064
|
'Updates an existing member in Ghost CMS. All fields except id are optional.',
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
.describe('New list of label names to assign to the member.'),
|
|
964
|
-
newsletters: z
|
|
965
|
-
.array(z.object({ id: z.string() }))
|
|
966
|
-
.optional()
|
|
967
|
-
.describe('New list of newsletter objects with id field to subscribe the member to.'),
|
|
968
|
-
},
|
|
969
|
-
async (input) => {
|
|
1065
|
+
updateMemberInputSchema,
|
|
1066
|
+
async (rawInput) => {
|
|
1067
|
+
const validation = validateToolInput(updateMemberInputSchema, rawInput, 'ghost_update_member');
|
|
1068
|
+
if (!validation.success) {
|
|
1069
|
+
return validation.errorResponse;
|
|
1070
|
+
}
|
|
1071
|
+
const input = validation.data;
|
|
1072
|
+
|
|
970
1073
|
console.error(`Executing tool: ghost_update_member for member ID: ${input.id}`);
|
|
971
1074
|
try {
|
|
972
1075
|
await loadServices();
|
|
973
1076
|
|
|
974
1077
|
const { id, ...updateData } = input;
|
|
975
1078
|
|
|
976
|
-
const
|
|
977
|
-
const updatedMember = await ghostServiceImproved.updateMember(id, updateData);
|
|
1079
|
+
const updatedMember = await ghostService.updateMember(id, updateData);
|
|
978
1080
|
console.error(`Member updated successfully. Member ID: ${updatedMember.id}`);
|
|
979
1081
|
|
|
980
1082
|
return {
|
|
@@ -982,6 +1084,13 @@ server.tool(
|
|
|
982
1084
|
};
|
|
983
1085
|
} catch (error) {
|
|
984
1086
|
console.error(`Error in ghost_update_member:`, error);
|
|
1087
|
+
if (error.name === 'ZodError') {
|
|
1088
|
+
const validationError = ValidationError.fromZod(error, 'Member update');
|
|
1089
|
+
return {
|
|
1090
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1091
|
+
isError: true,
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
985
1094
|
return {
|
|
986
1095
|
content: [{ type: 'text', text: `Error updating member: ${error.message}` }],
|
|
987
1096
|
isError: true,
|
|
@@ -994,16 +1103,19 @@ server.tool(
|
|
|
994
1103
|
server.tool(
|
|
995
1104
|
'ghost_delete_member',
|
|
996
1105
|
'Deletes a member from Ghost CMS by ID. This operation is permanent and cannot be undone.',
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1106
|
+
deleteMemberSchema,
|
|
1107
|
+
async (rawInput) => {
|
|
1108
|
+
const validation = validateToolInput(deleteMemberSchema, rawInput, 'ghost_delete_member');
|
|
1109
|
+
if (!validation.success) {
|
|
1110
|
+
return validation.errorResponse;
|
|
1111
|
+
}
|
|
1112
|
+
const { id } = validation.data;
|
|
1113
|
+
|
|
1001
1114
|
console.error(`Executing tool: ghost_delete_member for member ID: ${id}`);
|
|
1002
1115
|
try {
|
|
1003
1116
|
await loadServices();
|
|
1004
1117
|
|
|
1005
|
-
|
|
1006
|
-
await ghostServiceImproved.deleteMember(id);
|
|
1118
|
+
await ghostService.deleteMember(id);
|
|
1007
1119
|
console.error(`Member deleted successfully. Member ID: ${id}`);
|
|
1008
1120
|
|
|
1009
1121
|
return {
|
|
@@ -1011,6 +1123,13 @@ server.tool(
|
|
|
1011
1123
|
};
|
|
1012
1124
|
} catch (error) {
|
|
1013
1125
|
console.error(`Error in ghost_delete_member:`, error);
|
|
1126
|
+
if (error.name === 'ZodError') {
|
|
1127
|
+
const validationError = ValidationError.fromZod(error, 'Member deletion');
|
|
1128
|
+
return {
|
|
1129
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1130
|
+
isError: true,
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1014
1133
|
return {
|
|
1015
1134
|
content: [{ type: 'text', text: `Error deleting member: ${error.message}` }],
|
|
1016
1135
|
isError: true,
|
|
@@ -1023,25 +1142,14 @@ server.tool(
|
|
|
1023
1142
|
server.tool(
|
|
1024
1143
|
'ghost_get_members',
|
|
1025
1144
|
'Retrieves a list of members (subscribers) from Ghost CMS with optional filtering, pagination, and includes.',
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
.
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
filter: z
|
|
1035
|
-
.string()
|
|
1036
|
-
.optional()
|
|
1037
|
-
.describe('Ghost NQL filter string (e.g., "status:free", "status:paid", "subscribed:true").'),
|
|
1038
|
-
order: z.string().optional().describe('Order string (e.g., "created_at desc", "email asc").'),
|
|
1039
|
-
include: z
|
|
1040
|
-
.string()
|
|
1041
|
-
.optional()
|
|
1042
|
-
.describe('Comma-separated list of related data to include (e.g., "labels,newsletters").'),
|
|
1043
|
-
},
|
|
1044
|
-
async (input) => {
|
|
1145
|
+
getMembersSchema,
|
|
1146
|
+
async (rawInput) => {
|
|
1147
|
+
const validation = validateToolInput(getMembersSchema, rawInput, 'ghost_get_members');
|
|
1148
|
+
if (!validation.success) {
|
|
1149
|
+
return validation.errorResponse;
|
|
1150
|
+
}
|
|
1151
|
+
const input = validation.data;
|
|
1152
|
+
|
|
1045
1153
|
console.error(`Executing tool: ghost_get_members`);
|
|
1046
1154
|
try {
|
|
1047
1155
|
await loadServices();
|
|
@@ -1053,8 +1161,7 @@ server.tool(
|
|
|
1053
1161
|
if (input.order !== undefined) options.order = input.order;
|
|
1054
1162
|
if (input.include !== undefined) options.include = input.include;
|
|
1055
1163
|
|
|
1056
|
-
const
|
|
1057
|
-
const members = await ghostServiceImproved.getMembers(options);
|
|
1164
|
+
const members = await ghostService.getMembers(options);
|
|
1058
1165
|
console.error(`Retrieved ${members.length} members from Ghost.`);
|
|
1059
1166
|
|
|
1060
1167
|
return {
|
|
@@ -1062,6 +1169,13 @@ server.tool(
|
|
|
1062
1169
|
};
|
|
1063
1170
|
} catch (error) {
|
|
1064
1171
|
console.error(`Error in ghost_get_members:`, error);
|
|
1172
|
+
if (error.name === 'ZodError') {
|
|
1173
|
+
const validationError = ValidationError.fromZod(error, 'Member query');
|
|
1174
|
+
return {
|
|
1175
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1176
|
+
isError: true,
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1065
1179
|
return {
|
|
1066
1180
|
content: [{ type: 'text', text: `Error retrieving members: ${error.message}` }],
|
|
1067
1181
|
isError: true,
|
|
@@ -1074,17 +1188,19 @@ server.tool(
|
|
|
1074
1188
|
server.tool(
|
|
1075
1189
|
'ghost_get_member',
|
|
1076
1190
|
'Retrieves a single member from Ghost CMS by ID or email. Provide either id OR email.',
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1191
|
+
getMemberSchema,
|
|
1192
|
+
async (rawInput) => {
|
|
1193
|
+
const validation = validateToolInput(getMemberSchema, rawInput, 'ghost_get_member');
|
|
1194
|
+
if (!validation.success) {
|
|
1195
|
+
return validation.errorResponse;
|
|
1196
|
+
}
|
|
1197
|
+
const { id, email } = validation.data;
|
|
1198
|
+
|
|
1082
1199
|
console.error(`Executing tool: ghost_get_member for ${id ? `ID: ${id}` : `email: ${email}`}`);
|
|
1083
1200
|
try {
|
|
1084
1201
|
await loadServices();
|
|
1085
1202
|
|
|
1086
|
-
const
|
|
1087
|
-
const member = await ghostServiceImproved.getMember({ id, email });
|
|
1203
|
+
const member = await ghostService.getMember({ id, email });
|
|
1088
1204
|
console.error(`Retrieved member: ${member.email} (ID: ${member.id})`);
|
|
1089
1205
|
|
|
1090
1206
|
return {
|
|
@@ -1092,6 +1208,13 @@ server.tool(
|
|
|
1092
1208
|
};
|
|
1093
1209
|
} catch (error) {
|
|
1094
1210
|
console.error(`Error in ghost_get_member:`, error);
|
|
1211
|
+
if (error.name === 'ZodError') {
|
|
1212
|
+
const validationError = ValidationError.fromZod(error, 'Member lookup');
|
|
1213
|
+
return {
|
|
1214
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1215
|
+
isError: true,
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1095
1218
|
return {
|
|
1096
1219
|
content: [{ type: 'text', text: `Error retrieving member: ${error.message}` }],
|
|
1097
1220
|
isError: true,
|
|
@@ -1104,16 +1227,14 @@ server.tool(
|
|
|
1104
1227
|
server.tool(
|
|
1105
1228
|
'ghost_search_members',
|
|
1106
1229
|
'Searches for members by name or email in Ghost CMS.',
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
.
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
},
|
|
1116
|
-
async ({ query, limit }) => {
|
|
1230
|
+
searchMembersSchema,
|
|
1231
|
+
async (rawInput) => {
|
|
1232
|
+
const validation = validateToolInput(searchMembersSchema, rawInput, 'ghost_search_members');
|
|
1233
|
+
if (!validation.success) {
|
|
1234
|
+
return validation.errorResponse;
|
|
1235
|
+
}
|
|
1236
|
+
const { query, limit } = validation.data;
|
|
1237
|
+
|
|
1117
1238
|
console.error(`Executing tool: ghost_search_members with query: ${query}`);
|
|
1118
1239
|
try {
|
|
1119
1240
|
await loadServices();
|
|
@@ -1121,8 +1242,7 @@ server.tool(
|
|
|
1121
1242
|
const options = {};
|
|
1122
1243
|
if (limit !== undefined) options.limit = limit;
|
|
1123
1244
|
|
|
1124
|
-
const
|
|
1125
|
-
const members = await ghostServiceImproved.searchMembers(query, options);
|
|
1245
|
+
const members = await ghostService.searchMembers(query, options);
|
|
1126
1246
|
console.error(`Found ${members.length} members matching "${query}".`);
|
|
1127
1247
|
|
|
1128
1248
|
return {
|
|
@@ -1130,6 +1250,13 @@ server.tool(
|
|
|
1130
1250
|
};
|
|
1131
1251
|
} catch (error) {
|
|
1132
1252
|
console.error(`Error in ghost_search_members:`, error);
|
|
1253
|
+
if (error.name === 'ZodError') {
|
|
1254
|
+
const validationError = ValidationError.fromZod(error, 'Member search');
|
|
1255
|
+
return {
|
|
1256
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1257
|
+
isError: true,
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1133
1260
|
return {
|
|
1134
1261
|
content: [{ type: 'text', text: `Error searching members: ${error.message}` }],
|
|
1135
1262
|
isError: true,
|
|
@@ -1142,30 +1269,34 @@ server.tool(
|
|
|
1142
1269
|
// NEWSLETTER TOOLS
|
|
1143
1270
|
// =============================================================================
|
|
1144
1271
|
|
|
1272
|
+
// --- Newsletter Schema Definitions ---
|
|
1273
|
+
const getNewsletterSchema = z.object({ id: ghostIdSchema });
|
|
1274
|
+
const updateNewsletterInputSchema = z.object({ id: ghostIdSchema }).merge(updateNewsletterSchema);
|
|
1275
|
+
const deleteNewsletterSchema = z.object({ id: ghostIdSchema });
|
|
1276
|
+
|
|
1145
1277
|
// Get Newsletters Tool
|
|
1146
1278
|
server.tool(
|
|
1147
1279
|
'ghost_get_newsletters',
|
|
1148
1280
|
'Retrieves a list of newsletters from Ghost CMS with optional filtering.',
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
.
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
},
|
|
1158
|
-
async (input) => {
|
|
1281
|
+
newsletterQuerySchema,
|
|
1282
|
+
async (rawInput) => {
|
|
1283
|
+
const validation = validateToolInput(newsletterQuerySchema, rawInput, 'ghost_get_newsletters');
|
|
1284
|
+
if (!validation.success) {
|
|
1285
|
+
return validation.errorResponse;
|
|
1286
|
+
}
|
|
1287
|
+
const input = validation.data;
|
|
1288
|
+
|
|
1159
1289
|
console.error(`Executing tool: ghost_get_newsletters`);
|
|
1160
1290
|
try {
|
|
1161
1291
|
await loadServices();
|
|
1162
1292
|
|
|
1163
1293
|
const options = {};
|
|
1164
1294
|
if (input.limit !== undefined) options.limit = input.limit;
|
|
1295
|
+
if (input.page !== undefined) options.page = input.page;
|
|
1165
1296
|
if (input.filter !== undefined) options.filter = input.filter;
|
|
1297
|
+
if (input.order !== undefined) options.order = input.order;
|
|
1166
1298
|
|
|
1167
|
-
const
|
|
1168
|
-
const newsletters = await ghostServiceImproved.getNewsletters(options);
|
|
1299
|
+
const newsletters = await ghostService.getNewsletters(options);
|
|
1169
1300
|
console.error(`Retrieved ${newsletters.length} newsletters from Ghost.`);
|
|
1170
1301
|
|
|
1171
1302
|
return {
|
|
@@ -1173,6 +1304,13 @@ server.tool(
|
|
|
1173
1304
|
};
|
|
1174
1305
|
} catch (error) {
|
|
1175
1306
|
console.error(`Error in ghost_get_newsletters:`, error);
|
|
1307
|
+
if (error.name === 'ZodError') {
|
|
1308
|
+
const validationError = ValidationError.fromZod(error, 'Newsletter query');
|
|
1309
|
+
return {
|
|
1310
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1311
|
+
isError: true,
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1176
1314
|
return {
|
|
1177
1315
|
content: [{ type: 'text', text: `Error retrieving newsletters: ${error.message}` }],
|
|
1178
1316
|
isError: true,
|
|
@@ -1185,16 +1323,19 @@ server.tool(
|
|
|
1185
1323
|
server.tool(
|
|
1186
1324
|
'ghost_get_newsletter',
|
|
1187
1325
|
'Retrieves a single newsletter from Ghost CMS by ID.',
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1326
|
+
getNewsletterSchema,
|
|
1327
|
+
async (rawInput) => {
|
|
1328
|
+
const validation = validateToolInput(getNewsletterSchema, rawInput, 'ghost_get_newsletter');
|
|
1329
|
+
if (!validation.success) {
|
|
1330
|
+
return validation.errorResponse;
|
|
1331
|
+
}
|
|
1332
|
+
const { id } = validation.data;
|
|
1333
|
+
|
|
1192
1334
|
console.error(`Executing tool: ghost_get_newsletter for ID: ${id}`);
|
|
1193
1335
|
try {
|
|
1194
1336
|
await loadServices();
|
|
1195
1337
|
|
|
1196
|
-
const
|
|
1197
|
-
const newsletter = await ghostServiceImproved.getNewsletter(id);
|
|
1338
|
+
const newsletter = await ghostService.getNewsletter(id);
|
|
1198
1339
|
console.error(`Retrieved newsletter: ${newsletter.name} (ID: ${newsletter.id})`);
|
|
1199
1340
|
|
|
1200
1341
|
return {
|
|
@@ -1202,6 +1343,13 @@ server.tool(
|
|
|
1202
1343
|
};
|
|
1203
1344
|
} catch (error) {
|
|
1204
1345
|
console.error(`Error in ghost_get_newsletter:`, error);
|
|
1346
|
+
if (error.name === 'ZodError') {
|
|
1347
|
+
const validationError = ValidationError.fromZod(error, 'Newsletter retrieval');
|
|
1348
|
+
return {
|
|
1349
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1350
|
+
isError: true,
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1205
1353
|
return {
|
|
1206
1354
|
content: [{ type: 'text', text: `Error retrieving newsletter: ${error.message}` }],
|
|
1207
1355
|
isError: true,
|
|
@@ -1214,38 +1362,22 @@ server.tool(
|
|
|
1214
1362
|
server.tool(
|
|
1215
1363
|
'ghost_create_newsletter',
|
|
1216
1364
|
'Creates a new newsletter in Ghost CMS with customizable sender settings and display options.',
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
.describe('Reply-to address setting. Options: newsletter, support.'),
|
|
1230
|
-
subscribe_on_signup: z
|
|
1231
|
-
.boolean()
|
|
1232
|
-
.optional()
|
|
1233
|
-
.describe('Whether new members are automatically subscribed to this newsletter on signup.'),
|
|
1234
|
-
show_header_icon: z
|
|
1235
|
-
.boolean()
|
|
1236
|
-
.optional()
|
|
1237
|
-
.describe('Whether to show the site icon in the newsletter header.'),
|
|
1238
|
-
show_header_title: z
|
|
1239
|
-
.boolean()
|
|
1240
|
-
.optional()
|
|
1241
|
-
.describe('Whether to show the site title in the newsletter header.'),
|
|
1242
|
-
},
|
|
1243
|
-
async (input) => {
|
|
1365
|
+
createNewsletterSchema,
|
|
1366
|
+
async (rawInput) => {
|
|
1367
|
+
const validation = validateToolInput(
|
|
1368
|
+
createNewsletterSchema,
|
|
1369
|
+
rawInput,
|
|
1370
|
+
'ghost_create_newsletter'
|
|
1371
|
+
);
|
|
1372
|
+
if (!validation.success) {
|
|
1373
|
+
return validation.errorResponse;
|
|
1374
|
+
}
|
|
1375
|
+
const input = validation.data;
|
|
1376
|
+
|
|
1244
1377
|
console.error(`Executing tool: ghost_create_newsletter with name: ${input.name}`);
|
|
1245
1378
|
try {
|
|
1246
1379
|
await loadServices();
|
|
1247
1380
|
|
|
1248
|
-
const newsletterService = await import('./services/newsletterService.js');
|
|
1249
1381
|
const createdNewsletter = await newsletterService.createNewsletterService(input);
|
|
1250
1382
|
console.error(`Newsletter created successfully. Newsletter ID: ${createdNewsletter.id}`);
|
|
1251
1383
|
|
|
@@ -1254,6 +1386,13 @@ server.tool(
|
|
|
1254
1386
|
};
|
|
1255
1387
|
} catch (error) {
|
|
1256
1388
|
console.error(`Error in ghost_create_newsletter:`, error);
|
|
1389
|
+
if (error.name === 'ZodError') {
|
|
1390
|
+
const validationError = ValidationError.fromZod(error, 'Newsletter creation');
|
|
1391
|
+
return {
|
|
1392
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1393
|
+
isError: true,
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1257
1396
|
return {
|
|
1258
1397
|
content: [{ type: 'text', text: `Error creating newsletter: ${error.message}` }],
|
|
1259
1398
|
isError: true,
|
|
@@ -1266,26 +1405,25 @@ server.tool(
|
|
|
1266
1405
|
server.tool(
|
|
1267
1406
|
'ghost_update_newsletter',
|
|
1268
1407
|
'Updates an existing newsletter in Ghost CMS. Can update name, description, sender settings, and display options.',
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
.
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1408
|
+
updateNewsletterInputSchema,
|
|
1409
|
+
async (rawInput) => {
|
|
1410
|
+
const validation = validateToolInput(
|
|
1411
|
+
updateNewsletterInputSchema,
|
|
1412
|
+
rawInput,
|
|
1413
|
+
'ghost_update_newsletter'
|
|
1414
|
+
);
|
|
1415
|
+
if (!validation.success) {
|
|
1416
|
+
return validation.errorResponse;
|
|
1417
|
+
}
|
|
1418
|
+
const input = validation.data;
|
|
1419
|
+
|
|
1281
1420
|
console.error(`Executing tool: ghost_update_newsletter for newsletter ID: ${input.id}`);
|
|
1282
1421
|
try {
|
|
1283
1422
|
await loadServices();
|
|
1284
1423
|
|
|
1285
1424
|
const { id, ...updateData } = input;
|
|
1286
1425
|
|
|
1287
|
-
const
|
|
1288
|
-
const updatedNewsletter = await ghostServiceImproved.updateNewsletter(id, updateData);
|
|
1426
|
+
const updatedNewsletter = await ghostService.updateNewsletter(id, updateData);
|
|
1289
1427
|
console.error(`Newsletter updated successfully. Newsletter ID: ${updatedNewsletter.id}`);
|
|
1290
1428
|
|
|
1291
1429
|
return {
|
|
@@ -1293,6 +1431,13 @@ server.tool(
|
|
|
1293
1431
|
};
|
|
1294
1432
|
} catch (error) {
|
|
1295
1433
|
console.error(`Error in ghost_update_newsletter:`, error);
|
|
1434
|
+
if (error.name === 'ZodError') {
|
|
1435
|
+
const validationError = ValidationError.fromZod(error, 'Newsletter update');
|
|
1436
|
+
return {
|
|
1437
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1438
|
+
isError: true,
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1296
1441
|
return {
|
|
1297
1442
|
content: [{ type: 'text', text: `Error updating newsletter: ${error.message}` }],
|
|
1298
1443
|
isError: true,
|
|
@@ -1305,16 +1450,23 @@ server.tool(
|
|
|
1305
1450
|
server.tool(
|
|
1306
1451
|
'ghost_delete_newsletter',
|
|
1307
1452
|
'Deletes a newsletter from Ghost CMS by ID. This operation is permanent and cannot be undone.',
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1453
|
+
deleteNewsletterSchema,
|
|
1454
|
+
async (rawInput) => {
|
|
1455
|
+
const validation = validateToolInput(
|
|
1456
|
+
deleteNewsletterSchema,
|
|
1457
|
+
rawInput,
|
|
1458
|
+
'ghost_delete_newsletter'
|
|
1459
|
+
);
|
|
1460
|
+
if (!validation.success) {
|
|
1461
|
+
return validation.errorResponse;
|
|
1462
|
+
}
|
|
1463
|
+
const { id } = validation.data;
|
|
1464
|
+
|
|
1312
1465
|
console.error(`Executing tool: ghost_delete_newsletter for newsletter ID: ${id}`);
|
|
1313
1466
|
try {
|
|
1314
1467
|
await loadServices();
|
|
1315
1468
|
|
|
1316
|
-
|
|
1317
|
-
await ghostServiceImproved.deleteNewsletter(id);
|
|
1469
|
+
await ghostService.deleteNewsletter(id);
|
|
1318
1470
|
console.error(`Newsletter deleted successfully. Newsletter ID: ${id}`);
|
|
1319
1471
|
|
|
1320
1472
|
return {
|
|
@@ -1322,6 +1474,13 @@ server.tool(
|
|
|
1322
1474
|
};
|
|
1323
1475
|
} catch (error) {
|
|
1324
1476
|
console.error(`Error in ghost_delete_newsletter:`, error);
|
|
1477
|
+
if (error.name === 'ZodError') {
|
|
1478
|
+
const validationError = ValidationError.fromZod(error, 'Newsletter deletion');
|
|
1479
|
+
return {
|
|
1480
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1481
|
+
isError: true,
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1325
1484
|
return {
|
|
1326
1485
|
content: [{ type: 'text', text: `Error deleting newsletter: ${error.message}` }],
|
|
1327
1486
|
isError: true,
|
|
@@ -1332,27 +1491,28 @@ server.tool(
|
|
|
1332
1491
|
|
|
1333
1492
|
// --- Tier Tools ---
|
|
1334
1493
|
|
|
1494
|
+
// --- Tier Schema Definitions ---
|
|
1495
|
+
const getTierSchema = z.object({ id: ghostIdSchema });
|
|
1496
|
+
const updateTierInputSchema = z.object({ id: ghostIdSchema }).merge(updateTierSchema);
|
|
1497
|
+
const deleteTierSchema = z.object({ id: ghostIdSchema });
|
|
1498
|
+
|
|
1335
1499
|
// Get Tiers Tool
|
|
1336
1500
|
server.tool(
|
|
1337
1501
|
'ghost_get_tiers',
|
|
1338
1502
|
'Retrieves a list of tiers (membership levels) from Ghost CMS with optional filtering by type (free/paid).',
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
.
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
filter: z.string().optional().describe('NQL filter string (e.g., "type:paid" or "type:free")'),
|
|
1348
|
-
},
|
|
1349
|
-
async (input) => {
|
|
1503
|
+
tierQuerySchema,
|
|
1504
|
+
async (rawInput) => {
|
|
1505
|
+
const validation = validateToolInput(tierQuerySchema, rawInput, 'ghost_get_tiers');
|
|
1506
|
+
if (!validation.success) {
|
|
1507
|
+
return validation.errorResponse;
|
|
1508
|
+
}
|
|
1509
|
+
const input = validation.data;
|
|
1510
|
+
|
|
1350
1511
|
console.error(`Executing tool: ghost_get_tiers`);
|
|
1351
1512
|
try {
|
|
1352
1513
|
await loadServices();
|
|
1353
1514
|
|
|
1354
|
-
const
|
|
1355
|
-
const tiers = await ghostServiceImproved.getTiers(input);
|
|
1515
|
+
const tiers = await ghostService.getTiers(input);
|
|
1356
1516
|
console.error(`Retrieved ${tiers.length} tiers`);
|
|
1357
1517
|
|
|
1358
1518
|
return {
|
|
@@ -1360,6 +1520,13 @@ server.tool(
|
|
|
1360
1520
|
};
|
|
1361
1521
|
} catch (error) {
|
|
1362
1522
|
console.error(`Error in ghost_get_tiers:`, error);
|
|
1523
|
+
if (error.name === 'ZodError') {
|
|
1524
|
+
const validationError = ValidationError.fromZod(error, 'Tier query');
|
|
1525
|
+
return {
|
|
1526
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1527
|
+
isError: true,
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1363
1530
|
return {
|
|
1364
1531
|
content: [{ type: 'text', text: `Error getting tiers: ${error.message}` }],
|
|
1365
1532
|
isError: true,
|
|
@@ -1372,16 +1539,19 @@ server.tool(
|
|
|
1372
1539
|
server.tool(
|
|
1373
1540
|
'ghost_get_tier',
|
|
1374
1541
|
'Retrieves a single tier (membership level) from Ghost CMS by ID.',
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1542
|
+
getTierSchema,
|
|
1543
|
+
async (rawInput) => {
|
|
1544
|
+
const validation = validateToolInput(getTierSchema, rawInput, 'ghost_get_tier');
|
|
1545
|
+
if (!validation.success) {
|
|
1546
|
+
return validation.errorResponse;
|
|
1547
|
+
}
|
|
1548
|
+
const { id } = validation.data;
|
|
1549
|
+
|
|
1379
1550
|
console.error(`Executing tool: ghost_get_tier for tier ID: ${id}`);
|
|
1380
1551
|
try {
|
|
1381
1552
|
await loadServices();
|
|
1382
1553
|
|
|
1383
|
-
const
|
|
1384
|
-
const tier = await ghostServiceImproved.getTier(id);
|
|
1554
|
+
const tier = await ghostService.getTier(id);
|
|
1385
1555
|
console.error(`Tier retrieved successfully. Tier ID: ${tier.id}`);
|
|
1386
1556
|
|
|
1387
1557
|
return {
|
|
@@ -1389,6 +1559,13 @@ server.tool(
|
|
|
1389
1559
|
};
|
|
1390
1560
|
} catch (error) {
|
|
1391
1561
|
console.error(`Error in ghost_get_tier:`, error);
|
|
1562
|
+
if (error.name === 'ZodError') {
|
|
1563
|
+
const validationError = ValidationError.fromZod(error, 'Tier retrieval');
|
|
1564
|
+
return {
|
|
1565
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1566
|
+
isError: true,
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1392
1569
|
return {
|
|
1393
1570
|
content: [{ type: 'text', text: `Error getting tier: ${error.message}` }],
|
|
1394
1571
|
isError: true,
|
|
@@ -1401,32 +1578,19 @@ server.tool(
|
|
|
1401
1578
|
server.tool(
|
|
1402
1579
|
'ghost_create_tier',
|
|
1403
1580
|
'Creates a new tier (membership level) in Ghost CMS with pricing and benefits.',
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
.
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
.describe('Monthly price in cents (e.g., 500 = $5.00)'),
|
|
1413
|
-
yearly_price: z
|
|
1414
|
-
.number()
|
|
1415
|
-
.int()
|
|
1416
|
-
.min(0)
|
|
1417
|
-
.optional()
|
|
1418
|
-
.describe('Yearly price in cents (e.g., 5000 = $50.00)'),
|
|
1419
|
-
currency: z.string().length(3).optional().describe('Currency code (e.g., "USD", "EUR")'),
|
|
1420
|
-
benefits: z.array(z.string()).optional().describe('Array of benefit descriptions'),
|
|
1421
|
-
welcome_page_url: z.string().url().optional().describe('Welcome page URL for new subscribers'),
|
|
1422
|
-
},
|
|
1423
|
-
async (input) => {
|
|
1581
|
+
createTierSchema,
|
|
1582
|
+
async (rawInput) => {
|
|
1583
|
+
const validation = validateToolInput(createTierSchema, rawInput, 'ghost_create_tier');
|
|
1584
|
+
if (!validation.success) {
|
|
1585
|
+
return validation.errorResponse;
|
|
1586
|
+
}
|
|
1587
|
+
const input = validation.data;
|
|
1588
|
+
|
|
1424
1589
|
console.error(`Executing tool: ghost_create_tier`);
|
|
1425
1590
|
try {
|
|
1426
1591
|
await loadServices();
|
|
1427
1592
|
|
|
1428
|
-
const
|
|
1429
|
-
const tier = await ghostServiceImproved.createTier(input);
|
|
1593
|
+
const tier = await ghostService.createTier(input);
|
|
1430
1594
|
console.error(`Tier created successfully. Tier ID: ${tier.id}`);
|
|
1431
1595
|
|
|
1432
1596
|
return {
|
|
@@ -1434,6 +1598,13 @@ server.tool(
|
|
|
1434
1598
|
};
|
|
1435
1599
|
} catch (error) {
|
|
1436
1600
|
console.error(`Error in ghost_create_tier:`, error);
|
|
1601
|
+
if (error.name === 'ZodError') {
|
|
1602
|
+
const validationError = ValidationError.fromZod(error, 'Tier creation');
|
|
1603
|
+
return {
|
|
1604
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1605
|
+
isError: true,
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1437
1608
|
return {
|
|
1438
1609
|
content: [{ type: 'text', text: `Error creating tier: ${error.message}` }],
|
|
1439
1610
|
isError: true,
|
|
@@ -1446,24 +1617,21 @@ server.tool(
|
|
|
1446
1617
|
server.tool(
|
|
1447
1618
|
'ghost_update_tier',
|
|
1448
1619
|
'Updates an existing tier (membership level) in Ghost CMS. Can update pricing, benefits, and other tier properties.',
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
},
|
|
1458
|
-
async (input) => {
|
|
1620
|
+
updateTierInputSchema,
|
|
1621
|
+
async (rawInput) => {
|
|
1622
|
+
const validation = validateToolInput(updateTierInputSchema, rawInput, 'ghost_update_tier');
|
|
1623
|
+
if (!validation.success) {
|
|
1624
|
+
return validation.errorResponse;
|
|
1625
|
+
}
|
|
1626
|
+
const input = validation.data;
|
|
1627
|
+
|
|
1459
1628
|
console.error(`Executing tool: ghost_update_tier for tier ID: ${input.id}`);
|
|
1460
1629
|
try {
|
|
1461
1630
|
await loadServices();
|
|
1462
1631
|
|
|
1463
1632
|
const { id, ...updateData } = input;
|
|
1464
1633
|
|
|
1465
|
-
const
|
|
1466
|
-
const updatedTier = await ghostServiceImproved.updateTier(id, updateData);
|
|
1634
|
+
const updatedTier = await ghostService.updateTier(id, updateData);
|
|
1467
1635
|
console.error(`Tier updated successfully. Tier ID: ${updatedTier.id}`);
|
|
1468
1636
|
|
|
1469
1637
|
return {
|
|
@@ -1471,6 +1639,13 @@ server.tool(
|
|
|
1471
1639
|
};
|
|
1472
1640
|
} catch (error) {
|
|
1473
1641
|
console.error(`Error in ghost_update_tier:`, error);
|
|
1642
|
+
if (error.name === 'ZodError') {
|
|
1643
|
+
const validationError = ValidationError.fromZod(error, 'Tier update');
|
|
1644
|
+
return {
|
|
1645
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1646
|
+
isError: true,
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1474
1649
|
return {
|
|
1475
1650
|
content: [{ type: 'text', text: `Error updating tier: ${error.message}` }],
|
|
1476
1651
|
isError: true,
|
|
@@ -1483,16 +1658,19 @@ server.tool(
|
|
|
1483
1658
|
server.tool(
|
|
1484
1659
|
'ghost_delete_tier',
|
|
1485
1660
|
'Deletes a tier (membership level) from Ghost CMS by ID. This operation is permanent and cannot be undone.',
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1661
|
+
deleteTierSchema,
|
|
1662
|
+
async (rawInput) => {
|
|
1663
|
+
const validation = validateToolInput(deleteTierSchema, rawInput, 'ghost_delete_tier');
|
|
1664
|
+
if (!validation.success) {
|
|
1665
|
+
return validation.errorResponse;
|
|
1666
|
+
}
|
|
1667
|
+
const { id } = validation.data;
|
|
1668
|
+
|
|
1490
1669
|
console.error(`Executing tool: ghost_delete_tier for tier ID: ${id}`);
|
|
1491
1670
|
try {
|
|
1492
1671
|
await loadServices();
|
|
1493
1672
|
|
|
1494
|
-
|
|
1495
|
-
await ghostServiceImproved.deleteTier(id);
|
|
1673
|
+
await ghostService.deleteTier(id);
|
|
1496
1674
|
console.error(`Tier deleted successfully. Tier ID: ${id}`);
|
|
1497
1675
|
|
|
1498
1676
|
return {
|
|
@@ -1500,6 +1678,13 @@ server.tool(
|
|
|
1500
1678
|
};
|
|
1501
1679
|
} catch (error) {
|
|
1502
1680
|
console.error(`Error in ghost_delete_tier:`, error);
|
|
1681
|
+
if (error.name === 'ZodError') {
|
|
1682
|
+
const validationError = ValidationError.fromZod(error, 'Tier deletion');
|
|
1683
|
+
return {
|
|
1684
|
+
content: [{ type: 'text', text: JSON.stringify(validationError.toJSON(), null, 2) }],
|
|
1685
|
+
isError: true,
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1503
1688
|
return {
|
|
1504
1689
|
content: [{ type: 'text', text: `Error deleting tier: ${error.message}` }],
|
|
1505
1690
|
isError: true,
|