@jgardner04/ghost-mcp-server 1.12.0 → 1.12.2
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_improved.test.js +80 -16
- package/src/errors/index.js +7 -0
- package/src/mcp_server_improved.js +55 -80
- 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
|
@@ -40,13 +40,17 @@ dotenv.config();
|
|
|
40
40
|
// Lazy-loaded modules (to avoid Node.js v25 Buffer compatibility issues at startup)
|
|
41
41
|
let ghostService = null;
|
|
42
42
|
let postService = null;
|
|
43
|
+
let pageService = null;
|
|
44
|
+
let newsletterService = null;
|
|
43
45
|
let imageProcessingService = null;
|
|
44
46
|
let urlValidator = null;
|
|
45
47
|
|
|
46
48
|
const loadServices = async () => {
|
|
47
49
|
if (!ghostService) {
|
|
48
|
-
ghostService = await import('./services/
|
|
50
|
+
ghostService = await import('./services/ghostServiceImproved.js');
|
|
49
51
|
postService = await import('./services/postService.js');
|
|
52
|
+
pageService = await import('./services/pageService.js');
|
|
53
|
+
newsletterService = await import('./services/newsletterService.js');
|
|
50
54
|
imageProcessingService = await import('./services/imageProcessingService.js');
|
|
51
55
|
urlValidator = await import('./utils/urlValidator.js');
|
|
52
56
|
}
|
|
@@ -78,11 +82,18 @@ const server = new McpServer({
|
|
|
78
82
|
|
|
79
83
|
// --- Schema Definitions for Tools ---
|
|
80
84
|
const getTagsSchema = tagQuerySchema.partial();
|
|
81
|
-
const getTagSchema = z
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
const getTagSchema = z
|
|
86
|
+
.object({
|
|
87
|
+
id: ghostIdSchema.optional().describe('The ID of the tag to retrieve.'),
|
|
88
|
+
slug: z.string().optional().describe('The slug of the tag to retrieve.'),
|
|
89
|
+
include: z
|
|
90
|
+
.string()
|
|
91
|
+
.optional()
|
|
92
|
+
.describe('Additional resources to include (e.g., "count.posts").'),
|
|
93
|
+
})
|
|
94
|
+
.refine((data) => data.id || data.slug, {
|
|
95
|
+
message: 'Either id or slug is required to retrieve a tag',
|
|
96
|
+
});
|
|
86
97
|
const updateTagInputSchema = updateTagSchema.extend({ id: ghostIdSchema });
|
|
87
98
|
const deleteTagSchema = z.object({ id: ghostIdSchema });
|
|
88
99
|
|
|
@@ -183,18 +194,13 @@ server.tool(
|
|
|
183
194
|
|
|
184
195
|
console.error(`Executing tool: ghost_get_tag`);
|
|
185
196
|
try {
|
|
186
|
-
if (!id && !slug) {
|
|
187
|
-
throw new Error('Either id or slug must be provided');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
197
|
await loadServices();
|
|
191
198
|
|
|
192
199
|
// If slug is provided, use the slug/slug-name format
|
|
193
200
|
const identifier = slug ? `slug/${slug}` : id;
|
|
194
201
|
const options = include ? { include } : {};
|
|
195
202
|
|
|
196
|
-
const
|
|
197
|
-
const tag = await ghostServiceImproved.getTag(identifier, options);
|
|
203
|
+
const tag = await ghostService.getTag(identifier, options);
|
|
198
204
|
console.error(`Tag retrieved successfully. Tag ID: ${tag.id}`);
|
|
199
205
|
|
|
200
206
|
return {
|
|
@@ -240,8 +246,7 @@ server.tool(
|
|
|
240
246
|
// Build update data object with only provided fields (exclude id from update data)
|
|
241
247
|
const { id, ...updateData } = input;
|
|
242
248
|
|
|
243
|
-
const
|
|
244
|
-
const updatedTag = await ghostServiceImproved.updateTag(id, updateData);
|
|
249
|
+
const updatedTag = await ghostService.updateTag(id, updateData);
|
|
245
250
|
console.error(`Tag updated successfully. Tag ID: ${updatedTag.id}`);
|
|
246
251
|
|
|
247
252
|
return {
|
|
@@ -284,8 +289,7 @@ server.tool(
|
|
|
284
289
|
|
|
285
290
|
await loadServices();
|
|
286
291
|
|
|
287
|
-
|
|
288
|
-
await ghostServiceImproved.deleteTag(id);
|
|
292
|
+
await ghostService.deleteTag(id);
|
|
289
293
|
console.error(`Tag deleted successfully. Tag ID: ${id}`);
|
|
290
294
|
|
|
291
295
|
return {
|
|
@@ -408,14 +412,18 @@ const getPostsSchema = postQuerySchema.extend({
|
|
|
408
412
|
.optional()
|
|
409
413
|
.describe('Filter posts by status. Options: published, draft, scheduled, all.'),
|
|
410
414
|
});
|
|
411
|
-
const getPostSchema = z
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
415
|
+
const getPostSchema = z
|
|
416
|
+
.object({
|
|
417
|
+
id: ghostIdSchema.optional().describe('The ID of the post to retrieve.'),
|
|
418
|
+
slug: z.string().optional().describe('The slug of the post to retrieve.'),
|
|
419
|
+
include: z
|
|
420
|
+
.string()
|
|
421
|
+
.optional()
|
|
422
|
+
.describe('Comma-separated list of relations to include (e.g., "tags,authors").'),
|
|
423
|
+
})
|
|
424
|
+
.refine((data) => data.id || data.slug, {
|
|
425
|
+
message: 'Either id or slug is required to retrieve a post',
|
|
426
|
+
});
|
|
419
427
|
const searchPostsSchema = z.object({
|
|
420
428
|
query: z.string().min(1).describe('Search query to find in post titles.'),
|
|
421
429
|
status: z
|
|
@@ -533,11 +541,6 @@ server.tool(
|
|
|
533
541
|
|
|
534
542
|
console.error(`Executing tool: ghost_get_post`);
|
|
535
543
|
try {
|
|
536
|
-
// Validate that at least one of id or slug is provided
|
|
537
|
-
if (!input.id && !input.slug) {
|
|
538
|
-
throw new Error('Either id or slug is required to retrieve a post');
|
|
539
|
-
}
|
|
540
|
-
|
|
541
544
|
await loadServices();
|
|
542
545
|
|
|
543
546
|
// Build options object
|
|
@@ -591,9 +594,7 @@ server.tool(
|
|
|
591
594
|
if (input.status !== undefined) options.status = input.status;
|
|
592
595
|
if (input.limit !== undefined) options.limit = input.limit;
|
|
593
596
|
|
|
594
|
-
|
|
595
|
-
const ghostServiceImproved = await import('./services/ghostServiceImproved.js');
|
|
596
|
-
const posts = await ghostServiceImproved.searchPosts(input.query, options);
|
|
597
|
+
const posts = await ghostService.searchPosts(input.query, options);
|
|
597
598
|
console.error(`Found ${posts.length} posts matching "${input.query}".`);
|
|
598
599
|
|
|
599
600
|
return {
|
|
@@ -635,9 +636,7 @@ server.tool(
|
|
|
635
636
|
// Extract ID from input and build update data
|
|
636
637
|
const { id, ...updateData } = input;
|
|
637
638
|
|
|
638
|
-
|
|
639
|
-
const ghostServiceImproved = await import('./services/ghostServiceImproved.js');
|
|
640
|
-
const updatedPost = await ghostServiceImproved.updatePost(id, updateData);
|
|
639
|
+
const updatedPost = await ghostService.updatePost(id, updateData);
|
|
641
640
|
console.error(`Post updated successfully. Post ID: ${updatedPost.id}`);
|
|
642
641
|
|
|
643
642
|
return {
|
|
@@ -676,9 +675,7 @@ server.tool(
|
|
|
676
675
|
try {
|
|
677
676
|
await loadServices();
|
|
678
677
|
|
|
679
|
-
|
|
680
|
-
const ghostServiceImproved = await import('./services/ghostServiceImproved.js');
|
|
681
|
-
await ghostServiceImproved.deletePost(id);
|
|
678
|
+
await ghostService.deletePost(id);
|
|
682
679
|
console.error(`Post deleted successfully. Post ID: ${id}`);
|
|
683
680
|
|
|
684
681
|
return {
|
|
@@ -767,8 +764,7 @@ server.tool(
|
|
|
767
764
|
if (input.formats !== undefined) options.formats = input.formats;
|
|
768
765
|
if (input.order !== undefined) options.order = input.order;
|
|
769
766
|
|
|
770
|
-
const
|
|
771
|
-
const pages = await ghostServiceImproved.getPages(options);
|
|
767
|
+
const pages = await ghostService.getPages(options);
|
|
772
768
|
console.error(`Retrieved ${pages.length} pages from Ghost.`);
|
|
773
769
|
|
|
774
770
|
return {
|
|
@@ -812,8 +808,7 @@ server.tool(
|
|
|
812
808
|
|
|
813
809
|
const identifier = input.id || `slug/${input.slug}`;
|
|
814
810
|
|
|
815
|
-
const
|
|
816
|
-
const page = await ghostServiceImproved.getPage(identifier, options);
|
|
811
|
+
const page = await ghostService.getPage(identifier, options);
|
|
817
812
|
console.error(`Retrieved page: ${page.title} (ID: ${page.id})`);
|
|
818
813
|
|
|
819
814
|
return {
|
|
@@ -852,7 +847,6 @@ server.tool(
|
|
|
852
847
|
try {
|
|
853
848
|
await loadServices();
|
|
854
849
|
|
|
855
|
-
const pageService = await import('./services/pageService.js');
|
|
856
850
|
const createdPage = await pageService.createPageService(input);
|
|
857
851
|
console.error(`Page created successfully. Page ID: ${createdPage.id}`);
|
|
858
852
|
|
|
@@ -894,8 +888,7 @@ server.tool(
|
|
|
894
888
|
|
|
895
889
|
const { id, ...updateData } = input;
|
|
896
890
|
|
|
897
|
-
const
|
|
898
|
-
const updatedPage = await ghostServiceImproved.updatePage(id, updateData);
|
|
891
|
+
const updatedPage = await ghostService.updatePage(id, updateData);
|
|
899
892
|
console.error(`Page updated successfully. Page ID: ${updatedPage.id}`);
|
|
900
893
|
|
|
901
894
|
return {
|
|
@@ -934,8 +927,7 @@ server.tool(
|
|
|
934
927
|
try {
|
|
935
928
|
await loadServices();
|
|
936
929
|
|
|
937
|
-
|
|
938
|
-
await ghostServiceImproved.deletePage(id);
|
|
930
|
+
await ghostService.deletePage(id);
|
|
939
931
|
console.error(`Page deleted successfully. Page ID: ${id}`);
|
|
940
932
|
|
|
941
933
|
return {
|
|
@@ -978,8 +970,7 @@ server.tool(
|
|
|
978
970
|
if (input.status !== undefined) options.status = input.status;
|
|
979
971
|
if (input.limit !== undefined) options.limit = input.limit;
|
|
980
972
|
|
|
981
|
-
const
|
|
982
|
-
const pages = await ghostServiceImproved.searchPages(input.query, options);
|
|
973
|
+
const pages = await ghostService.searchPages(input.query, options);
|
|
983
974
|
console.error(`Found ${pages.length} pages matching "${input.query}".`);
|
|
984
975
|
|
|
985
976
|
return {
|
|
@@ -1046,8 +1037,7 @@ server.tool(
|
|
|
1046
1037
|
try {
|
|
1047
1038
|
await loadServices();
|
|
1048
1039
|
|
|
1049
|
-
const
|
|
1050
|
-
const createdMember = await ghostServiceImproved.createMember(input);
|
|
1040
|
+
const createdMember = await ghostService.createMember(input);
|
|
1051
1041
|
console.error(`Member created successfully. Member ID: ${createdMember.id}`);
|
|
1052
1042
|
|
|
1053
1043
|
return {
|
|
@@ -1088,8 +1078,7 @@ server.tool(
|
|
|
1088
1078
|
|
|
1089
1079
|
const { id, ...updateData } = input;
|
|
1090
1080
|
|
|
1091
|
-
const
|
|
1092
|
-
const updatedMember = await ghostServiceImproved.updateMember(id, updateData);
|
|
1081
|
+
const updatedMember = await ghostService.updateMember(id, updateData);
|
|
1093
1082
|
console.error(`Member updated successfully. Member ID: ${updatedMember.id}`);
|
|
1094
1083
|
|
|
1095
1084
|
return {
|
|
@@ -1128,8 +1117,7 @@ server.tool(
|
|
|
1128
1117
|
try {
|
|
1129
1118
|
await loadServices();
|
|
1130
1119
|
|
|
1131
|
-
|
|
1132
|
-
await ghostServiceImproved.deleteMember(id);
|
|
1120
|
+
await ghostService.deleteMember(id);
|
|
1133
1121
|
console.error(`Member deleted successfully. Member ID: ${id}`);
|
|
1134
1122
|
|
|
1135
1123
|
return {
|
|
@@ -1175,8 +1163,7 @@ server.tool(
|
|
|
1175
1163
|
if (input.order !== undefined) options.order = input.order;
|
|
1176
1164
|
if (input.include !== undefined) options.include = input.include;
|
|
1177
1165
|
|
|
1178
|
-
const
|
|
1179
|
-
const members = await ghostServiceImproved.getMembers(options);
|
|
1166
|
+
const members = await ghostService.getMembers(options);
|
|
1180
1167
|
console.error(`Retrieved ${members.length} members from Ghost.`);
|
|
1181
1168
|
|
|
1182
1169
|
return {
|
|
@@ -1215,8 +1202,7 @@ server.tool(
|
|
|
1215
1202
|
try {
|
|
1216
1203
|
await loadServices();
|
|
1217
1204
|
|
|
1218
|
-
const
|
|
1219
|
-
const member = await ghostServiceImproved.getMember({ id, email });
|
|
1205
|
+
const member = await ghostService.getMember({ id, email });
|
|
1220
1206
|
console.error(`Retrieved member: ${member.email} (ID: ${member.id})`);
|
|
1221
1207
|
|
|
1222
1208
|
return {
|
|
@@ -1258,8 +1244,7 @@ server.tool(
|
|
|
1258
1244
|
const options = {};
|
|
1259
1245
|
if (limit !== undefined) options.limit = limit;
|
|
1260
1246
|
|
|
1261
|
-
const
|
|
1262
|
-
const members = await ghostServiceImproved.searchMembers(query, options);
|
|
1247
|
+
const members = await ghostService.searchMembers(query, options);
|
|
1263
1248
|
console.error(`Found ${members.length} members matching "${query}".`);
|
|
1264
1249
|
|
|
1265
1250
|
return {
|
|
@@ -1313,8 +1298,7 @@ server.tool(
|
|
|
1313
1298
|
if (input.filter !== undefined) options.filter = input.filter;
|
|
1314
1299
|
if (input.order !== undefined) options.order = input.order;
|
|
1315
1300
|
|
|
1316
|
-
const
|
|
1317
|
-
const newsletters = await ghostServiceImproved.getNewsletters(options);
|
|
1301
|
+
const newsletters = await ghostService.getNewsletters(options);
|
|
1318
1302
|
console.error(`Retrieved ${newsletters.length} newsletters from Ghost.`);
|
|
1319
1303
|
|
|
1320
1304
|
return {
|
|
@@ -1353,8 +1337,7 @@ server.tool(
|
|
|
1353
1337
|
try {
|
|
1354
1338
|
await loadServices();
|
|
1355
1339
|
|
|
1356
|
-
const
|
|
1357
|
-
const newsletter = await ghostServiceImproved.getNewsletter(id);
|
|
1340
|
+
const newsletter = await ghostService.getNewsletter(id);
|
|
1358
1341
|
console.error(`Retrieved newsletter: ${newsletter.name} (ID: ${newsletter.id})`);
|
|
1359
1342
|
|
|
1360
1343
|
return {
|
|
@@ -1397,7 +1380,6 @@ server.tool(
|
|
|
1397
1380
|
try {
|
|
1398
1381
|
await loadServices();
|
|
1399
1382
|
|
|
1400
|
-
const newsletterService = await import('./services/newsletterService.js');
|
|
1401
1383
|
const createdNewsletter = await newsletterService.createNewsletterService(input);
|
|
1402
1384
|
console.error(`Newsletter created successfully. Newsletter ID: ${createdNewsletter.id}`);
|
|
1403
1385
|
|
|
@@ -1443,8 +1425,7 @@ server.tool(
|
|
|
1443
1425
|
|
|
1444
1426
|
const { id, ...updateData } = input;
|
|
1445
1427
|
|
|
1446
|
-
const
|
|
1447
|
-
const updatedNewsletter = await ghostServiceImproved.updateNewsletter(id, updateData);
|
|
1428
|
+
const updatedNewsletter = await ghostService.updateNewsletter(id, updateData);
|
|
1448
1429
|
console.error(`Newsletter updated successfully. Newsletter ID: ${updatedNewsletter.id}`);
|
|
1449
1430
|
|
|
1450
1431
|
return {
|
|
@@ -1487,8 +1468,7 @@ server.tool(
|
|
|
1487
1468
|
try {
|
|
1488
1469
|
await loadServices();
|
|
1489
1470
|
|
|
1490
|
-
|
|
1491
|
-
await ghostServiceImproved.deleteNewsletter(id);
|
|
1471
|
+
await ghostService.deleteNewsletter(id);
|
|
1492
1472
|
console.error(`Newsletter deleted successfully. Newsletter ID: ${id}`);
|
|
1493
1473
|
|
|
1494
1474
|
return {
|
|
@@ -1534,8 +1514,7 @@ server.tool(
|
|
|
1534
1514
|
try {
|
|
1535
1515
|
await loadServices();
|
|
1536
1516
|
|
|
1537
|
-
const
|
|
1538
|
-
const tiers = await ghostServiceImproved.getTiers(input);
|
|
1517
|
+
const tiers = await ghostService.getTiers(input);
|
|
1539
1518
|
console.error(`Retrieved ${tiers.length} tiers`);
|
|
1540
1519
|
|
|
1541
1520
|
return {
|
|
@@ -1574,8 +1553,7 @@ server.tool(
|
|
|
1574
1553
|
try {
|
|
1575
1554
|
await loadServices();
|
|
1576
1555
|
|
|
1577
|
-
const
|
|
1578
|
-
const tier = await ghostServiceImproved.getTier(id);
|
|
1556
|
+
const tier = await ghostService.getTier(id);
|
|
1579
1557
|
console.error(`Tier retrieved successfully. Tier ID: ${tier.id}`);
|
|
1580
1558
|
|
|
1581
1559
|
return {
|
|
@@ -1614,8 +1592,7 @@ server.tool(
|
|
|
1614
1592
|
try {
|
|
1615
1593
|
await loadServices();
|
|
1616
1594
|
|
|
1617
|
-
const
|
|
1618
|
-
const tier = await ghostServiceImproved.createTier(input);
|
|
1595
|
+
const tier = await ghostService.createTier(input);
|
|
1619
1596
|
console.error(`Tier created successfully. Tier ID: ${tier.id}`);
|
|
1620
1597
|
|
|
1621
1598
|
return {
|
|
@@ -1656,8 +1633,7 @@ server.tool(
|
|
|
1656
1633
|
|
|
1657
1634
|
const { id, ...updateData } = input;
|
|
1658
1635
|
|
|
1659
|
-
const
|
|
1660
|
-
const updatedTier = await ghostServiceImproved.updateTier(id, updateData);
|
|
1636
|
+
const updatedTier = await ghostService.updateTier(id, updateData);
|
|
1661
1637
|
console.error(`Tier updated successfully. Tier ID: ${updatedTier.id}`);
|
|
1662
1638
|
|
|
1663
1639
|
return {
|
|
@@ -1696,8 +1672,7 @@ server.tool(
|
|
|
1696
1672
|
try {
|
|
1697
1673
|
await loadServices();
|
|
1698
1674
|
|
|
1699
|
-
|
|
1700
|
-
await ghostServiceImproved.deleteTier(id);
|
|
1675
|
+
await ghostService.deleteTier(id);
|
|
1701
1676
|
console.error(`Tier deleted successfully. Tier ID: ${id}`);
|
|
1702
1677
|
|
|
1703
1678
|
return {
|