@jgardner04/ghost-mcp-server 1.12.5 → 1.13.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/src/mcp_server.js CHANGED
@@ -14,7 +14,7 @@ import { trackTempFile, cleanupTempFiles } from './utils/tempFileManager.js';
14
14
  import {
15
15
  createTagSchema,
16
16
  updateTagSchema,
17
- tagQuerySchema,
17
+ tagQueryBaseSchema,
18
18
  ghostIdSchema,
19
19
  emailSchema,
20
20
  createPostSchema,
@@ -72,6 +72,17 @@ const getDefaultAltText = (filePath) => {
72
72
  }
73
73
  };
74
74
 
75
+ /**
76
+ * Escapes single quotes in NQL filter values by doubling them.
77
+ * This prevents filter injection attacks when building NQL query strings.
78
+ * Example: "O'Reilly" becomes "O''Reilly" for use in name:'O''Reilly'
79
+ * @param {string} value - The value to escape
80
+ * @returns {string} The escaped value safe for NQL filter strings
81
+ */
82
+ const escapeNqlValue = (value) => {
83
+ return value.replace(/'/g, "''");
84
+ };
85
+
75
86
  // Create server instance with new API
76
87
  const server = new McpServer({
77
88
  name: 'ghost-mcp-server',
@@ -81,7 +92,7 @@ const server = new McpServer({
81
92
  // --- Register Tools ---
82
93
 
83
94
  // --- Schema Definitions for Tools ---
84
- const getTagsSchema = tagQuerySchema.partial();
95
+ const getTagsSchema = tagQueryBaseSchema.partial();
85
96
  const getTagSchema = z
86
97
  .object({
87
98
  id: ghostIdSchema.optional().describe('The ID of the tag to retrieve.'),
@@ -98,10 +109,13 @@ const updateTagInputSchema = updateTagSchema.extend({ id: ghostIdSchema });
98
109
  const deleteTagSchema = z.object({ id: ghostIdSchema });
99
110
 
100
111
  // Get Tags Tool
101
- server.tool(
112
+ server.registerTool(
102
113
  'ghost_get_tags',
103
- 'Retrieves a list of tags from Ghost CMS. Can optionally filter by tag name.',
104
- getTagsSchema,
114
+ {
115
+ description:
116
+ 'Retrieves a list of tags from Ghost CMS with pagination, filtering, sorting, and relation inclusion. Supports filtering by name, slug, visibility, or custom NQL filter expressions.',
117
+ inputSchema: getTagsSchema,
118
+ },
105
119
  async (rawInput) => {
106
120
  const validation = validateToolInput(getTagsSchema, rawInput, 'ghost_get_tags');
107
121
  if (!validation.success) {
@@ -112,16 +126,30 @@ server.tool(
112
126
  console.error(`Executing tool: ghost_get_tags`);
113
127
  try {
114
128
  await loadServices();
115
- const tags = await ghostService.getTags();
116
- let result = tags;
117
-
118
- if (input.name) {
119
- result = tags.filter((tag) => tag.name.toLowerCase() === input.name.toLowerCase());
120
- console.error(`Filtered tags by name "${input.name}". Found ${result.length} match(es).`);
121
- } else {
122
- console.error(`Retrieved ${tags.length} tags from Ghost.`);
129
+
130
+ // Build options object with provided parameters
131
+ const options = {};
132
+ if (input.limit !== undefined) options.limit = input.limit;
133
+ if (input.page !== undefined) options.page = input.page;
134
+ if (input.order !== undefined) options.order = input.order;
135
+ if (input.include !== undefined) options.include = input.include;
136
+
137
+ // Build filter string from individual filter parameters
138
+ const filters = [];
139
+ if (input.name) filters.push(`name:'${escapeNqlValue(input.name)}'`);
140
+ if (input.slug) filters.push(`slug:'${escapeNqlValue(input.slug)}'`);
141
+ if (input.visibility) filters.push(`visibility:'${input.visibility}'`); // visibility is enum-validated, no escaping needed
142
+ if (input.filter) filters.push(input.filter);
143
+
144
+ if (filters.length > 0) {
145
+ options.filter = filters.join('+');
123
146
  }
124
147
 
148
+ const tags = await ghostService.getTags(options);
149
+ console.error(`Retrieved ${tags.length} tags from Ghost.`);
150
+
151
+ const result = tags;
152
+
125
153
  return {
126
154
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
127
155
  };
@@ -143,10 +171,12 @@ server.tool(
143
171
  );
144
172
 
145
173
  // Create Tag Tool
146
- server.tool(
174
+ server.registerTool(
147
175
  'ghost_create_tag',
148
- 'Creates a new tag in Ghost CMS.',
149
- createTagSchema,
176
+ {
177
+ description: 'Creates a new tag in Ghost CMS.',
178
+ inputSchema: createTagSchema,
179
+ },
150
180
  async (rawInput) => {
151
181
  const validation = validateToolInput(createTagSchema, rawInput, 'ghost_create_tag');
152
182
  if (!validation.success) {
@@ -181,10 +211,12 @@ server.tool(
181
211
  );
182
212
 
183
213
  // Get Tag Tool
184
- server.tool(
214
+ server.registerTool(
185
215
  'ghost_get_tag',
186
- 'Retrieves a single tag from Ghost CMS by ID or slug.',
187
- getTagSchema,
216
+ {
217
+ description: 'Retrieves a single tag from Ghost CMS by ID or slug.',
218
+ inputSchema: getTagSchema,
219
+ },
188
220
  async (rawInput) => {
189
221
  const validation = validateToolInput(getTagSchema, rawInput, 'ghost_get_tag');
190
222
  if (!validation.success) {
@@ -224,10 +256,12 @@ server.tool(
224
256
  );
225
257
 
226
258
  // Update Tag Tool
227
- server.tool(
259
+ server.registerTool(
228
260
  'ghost_update_tag',
229
- 'Updates an existing tag in Ghost CMS.',
230
- updateTagInputSchema,
261
+ {
262
+ description: 'Updates an existing tag in Ghost CMS.',
263
+ inputSchema: updateTagInputSchema,
264
+ },
231
265
  async (rawInput) => {
232
266
  const validation = validateToolInput(updateTagInputSchema, rawInput, 'ghost_update_tag');
233
267
  if (!validation.success) {
@@ -270,10 +304,12 @@ server.tool(
270
304
  );
271
305
 
272
306
  // Delete Tag Tool
273
- server.tool(
307
+ server.registerTool(
274
308
  'ghost_delete_tag',
275
- 'Deletes a tag from Ghost CMS by ID. This operation is permanent.',
276
- deleteTagSchema,
309
+ {
310
+ description: 'Deletes a tag from Ghost CMS by ID. This operation is permanent.',
311
+ inputSchema: deleteTagSchema,
312
+ },
277
313
  async (rawInput) => {
278
314
  const validation = validateToolInput(deleteTagSchema, rawInput, 'ghost_delete_tag');
279
315
  if (!validation.success) {
@@ -322,10 +358,13 @@ const uploadImageSchema = z.object({
322
358
  });
323
359
 
324
360
  // Upload Image Tool
325
- server.tool(
361
+ server.registerTool(
326
362
  'ghost_upload_image',
327
- 'Downloads an image from a URL, processes it, uploads it to Ghost CMS, and returns the final Ghost image URL and alt text.',
328
- uploadImageSchema,
363
+ {
364
+ description:
365
+ 'Downloads an image from a URL, processes it, uploads it to Ghost CMS, and returns the final Ghost image URL and alt text.',
366
+ inputSchema: uploadImageSchema,
367
+ },
329
368
  async (rawInput) => {
330
369
  const validation = validateToolInput(uploadImageSchema, rawInput, 'ghost_upload_image');
331
370
  if (!validation.success) {
@@ -442,10 +481,12 @@ const updatePostInputSchema = updatePostSchema.extend({ id: ghostIdSchema });
442
481
  const deletePostSchema = z.object({ id: ghostIdSchema });
443
482
 
444
483
  // Create Post Tool
445
- server.tool(
484
+ server.registerTool(
446
485
  'ghost_create_post',
447
- 'Creates a new post in Ghost CMS.',
448
- createPostSchema,
486
+ {
487
+ description: 'Creates a new post in Ghost CMS.',
488
+ inputSchema: createPostSchema,
489
+ },
449
490
  async (rawInput) => {
450
491
  const validation = validateToolInput(createPostSchema, rawInput, 'ghost_create_post');
451
492
  if (!validation.success) {
@@ -480,10 +521,13 @@ server.tool(
480
521
  );
481
522
 
482
523
  // Get Posts Tool
483
- server.tool(
524
+ server.registerTool(
484
525
  'ghost_get_posts',
485
- 'Retrieves a list of posts from Ghost CMS with pagination, filtering, and sorting options.',
486
- getPostsSchema,
526
+ {
527
+ description:
528
+ 'Retrieves a list of posts from Ghost CMS with pagination, filtering, and sorting options.',
529
+ inputSchema: getPostsSchema,
530
+ },
487
531
  async (rawInput) => {
488
532
  const validation = validateToolInput(getPostsSchema, rawInput, 'ghost_get_posts');
489
533
  if (!validation.success) {
@@ -503,6 +547,8 @@ server.tool(
503
547
  if (input.include !== undefined) options.include = input.include;
504
548
  if (input.filter !== undefined) options.filter = input.filter;
505
549
  if (input.order !== undefined) options.order = input.order;
550
+ if (input.fields !== undefined) options.fields = input.fields;
551
+ if (input.formats !== undefined) options.formats = input.formats;
506
552
 
507
553
  const posts = await ghostService.getPosts(options);
508
554
  console.error(`Retrieved ${posts.length} posts from Ghost.`);
@@ -528,10 +574,12 @@ server.tool(
528
574
  );
529
575
 
530
576
  // Get Post Tool
531
- server.tool(
577
+ server.registerTool(
532
578
  'ghost_get_post',
533
- 'Retrieves a single post from Ghost CMS by ID or slug.',
534
- getPostSchema,
579
+ {
580
+ description: 'Retrieves a single post from Ghost CMS by ID or slug.',
581
+ inputSchema: getPostSchema,
582
+ },
535
583
  async (rawInput) => {
536
584
  const validation = validateToolInput(getPostSchema, rawInput, 'ghost_get_post');
537
585
  if (!validation.success) {
@@ -574,10 +622,12 @@ server.tool(
574
622
  );
575
623
 
576
624
  // Search Posts Tool
577
- server.tool(
625
+ server.registerTool(
578
626
  'ghost_search_posts',
579
- 'Search for posts in Ghost CMS by query string with optional status filtering.',
580
- searchPostsSchema,
627
+ {
628
+ description: 'Search for posts in Ghost CMS by query string with optional status filtering.',
629
+ inputSchema: searchPostsSchema,
630
+ },
581
631
  async (rawInput) => {
582
632
  const validation = validateToolInput(searchPostsSchema, rawInput, 'ghost_search_posts');
583
633
  if (!validation.success) {
@@ -618,10 +668,13 @@ server.tool(
618
668
  );
619
669
 
620
670
  // Update Post Tool
621
- server.tool(
671
+ server.registerTool(
622
672
  'ghost_update_post',
623
- 'Updates an existing post in Ghost CMS. Can update title, content, status, tags, images, and SEO fields.',
624
- updatePostInputSchema,
673
+ {
674
+ description:
675
+ 'Updates an existing post in Ghost CMS. Can update title, content, status, tags, images, and SEO fields.',
676
+ inputSchema: updatePostInputSchema,
677
+ },
625
678
  async (rawInput) => {
626
679
  const validation = validateToolInput(updatePostInputSchema, rawInput, 'ghost_update_post');
627
680
  if (!validation.success) {
@@ -660,10 +713,13 @@ server.tool(
660
713
  );
661
714
 
662
715
  // Delete Post Tool
663
- server.tool(
716
+ server.registerTool(
664
717
  'ghost_delete_post',
665
- 'Deletes a post from Ghost CMS by ID. This operation is permanent and cannot be undone.',
666
- deletePostSchema,
718
+ {
719
+ description:
720
+ 'Deletes a post from Ghost CMS by ID. This operation is permanent and cannot be undone.',
721
+ inputSchema: deletePostSchema,
722
+ },
667
723
  async (rawInput) => {
668
724
  const validation = validateToolInput(deletePostSchema, rawInput, 'ghost_delete_post');
669
725
  if (!validation.success) {
@@ -740,10 +796,13 @@ const searchPagesSchema = z.object({
740
796
  });
741
797
 
742
798
  // Get Pages Tool
743
- server.tool(
799
+ server.registerTool(
744
800
  'ghost_get_pages',
745
- 'Retrieves a list of pages from Ghost CMS with pagination, filtering, and sorting options.',
746
- pageQuerySchema,
801
+ {
802
+ description:
803
+ 'Retrieves a list of pages from Ghost CMS with pagination, filtering, and sorting options.',
804
+ inputSchema: pageQuerySchema,
805
+ },
747
806
  async (rawInput) => {
748
807
  const validation = validateToolInput(pageQuerySchema, rawInput, 'ghost_get_pages');
749
808
  if (!validation.success) {
@@ -788,10 +847,12 @@ server.tool(
788
847
  );
789
848
 
790
849
  // Get Page Tool
791
- server.tool(
850
+ server.registerTool(
792
851
  'ghost_get_page',
793
- 'Retrieves a single page from Ghost CMS by ID or slug.',
794
- getPageSchema,
852
+ {
853
+ description: 'Retrieves a single page from Ghost CMS by ID or slug.',
854
+ inputSchema: getPageSchema,
855
+ },
795
856
  async (rawInput) => {
796
857
  const validation = validateToolInput(getPageSchema, rawInput, 'ghost_get_page');
797
858
  if (!validation.success) {
@@ -832,10 +893,13 @@ server.tool(
832
893
  );
833
894
 
834
895
  // Create Page Tool
835
- server.tool(
896
+ server.registerTool(
836
897
  'ghost_create_page',
837
- 'Creates a new page in Ghost CMS. Note: Pages do NOT typically use tags (unlike posts).',
838
- createPageSchema,
898
+ {
899
+ description:
900
+ 'Creates a new page in Ghost CMS. Note: Pages do NOT typically use tags (unlike posts).',
901
+ inputSchema: createPageSchema,
902
+ },
839
903
  async (rawInput) => {
840
904
  const validation = validateToolInput(createPageSchema, rawInput, 'ghost_create_page');
841
905
  if (!validation.success) {
@@ -871,10 +935,13 @@ server.tool(
871
935
  );
872
936
 
873
937
  // Update Page Tool
874
- server.tool(
938
+ server.registerTool(
875
939
  'ghost_update_page',
876
- 'Updates an existing page in Ghost CMS. Can update title, content, status, images, and SEO fields.',
877
- updatePageInputSchema,
940
+ {
941
+ description:
942
+ 'Updates an existing page in Ghost CMS. Can update title, content, status, images, and SEO fields.',
943
+ inputSchema: updatePageInputSchema,
944
+ },
878
945
  async (rawInput) => {
879
946
  const validation = validateToolInput(updatePageInputSchema, rawInput, 'ghost_update_page');
880
947
  if (!validation.success) {
@@ -912,10 +979,13 @@ server.tool(
912
979
  );
913
980
 
914
981
  // Delete Page Tool
915
- server.tool(
982
+ server.registerTool(
916
983
  'ghost_delete_page',
917
- 'Deletes a page from Ghost CMS by ID. This operation is permanent and cannot be undone.',
918
- deletePageSchema,
984
+ {
985
+ description:
986
+ 'Deletes a page from Ghost CMS by ID. This operation is permanent and cannot be undone.',
987
+ inputSchema: deletePageSchema,
988
+ },
919
989
  async (rawInput) => {
920
990
  const validation = validateToolInput(deletePageSchema, rawInput, 'ghost_delete_page');
921
991
  if (!validation.success) {
@@ -951,10 +1021,12 @@ server.tool(
951
1021
  );
952
1022
 
953
1023
  // Search Pages Tool
954
- server.tool(
1024
+ server.registerTool(
955
1025
  'ghost_search_pages',
956
- 'Search for pages in Ghost CMS by query string with optional status filtering.',
957
- searchPagesSchema,
1026
+ {
1027
+ description: 'Search for pages in Ghost CMS by query string with optional status filtering.',
1028
+ inputSchema: searchPagesSchema,
1029
+ },
958
1030
  async (rawInput) => {
959
1031
  const validation = validateToolInput(searchPagesSchema, rawInput, 'ghost_search_pages');
960
1032
  if (!validation.success) {
@@ -1022,10 +1094,12 @@ const searchMembersSchema = z.object({
1022
1094
  });
1023
1095
 
1024
1096
  // Create Member Tool
1025
- server.tool(
1097
+ server.registerTool(
1026
1098
  'ghost_create_member',
1027
- 'Creates a new member (subscriber) in Ghost CMS.',
1028
- createMemberSchema,
1099
+ {
1100
+ description: 'Creates a new member (subscriber) in Ghost CMS.',
1101
+ inputSchema: createMemberSchema,
1102
+ },
1029
1103
  async (rawInput) => {
1030
1104
  const validation = validateToolInput(createMemberSchema, rawInput, 'ghost_create_member');
1031
1105
  if (!validation.success) {
@@ -1061,10 +1135,12 @@ server.tool(
1061
1135
  );
1062
1136
 
1063
1137
  // Update Member Tool
1064
- server.tool(
1138
+ server.registerTool(
1065
1139
  'ghost_update_member',
1066
- 'Updates an existing member in Ghost CMS. All fields except id are optional.',
1067
- updateMemberInputSchema,
1140
+ {
1141
+ description: 'Updates an existing member in Ghost CMS. All fields except id are optional.',
1142
+ inputSchema: updateMemberInputSchema,
1143
+ },
1068
1144
  async (rawInput) => {
1069
1145
  const validation = validateToolInput(updateMemberInputSchema, rawInput, 'ghost_update_member');
1070
1146
  if (!validation.success) {
@@ -1102,10 +1178,13 @@ server.tool(
1102
1178
  );
1103
1179
 
1104
1180
  // Delete Member Tool
1105
- server.tool(
1181
+ server.registerTool(
1106
1182
  'ghost_delete_member',
1107
- 'Deletes a member from Ghost CMS by ID. This operation is permanent and cannot be undone.',
1108
- deleteMemberSchema,
1183
+ {
1184
+ description:
1185
+ 'Deletes a member from Ghost CMS by ID. This operation is permanent and cannot be undone.',
1186
+ inputSchema: deleteMemberSchema,
1187
+ },
1109
1188
  async (rawInput) => {
1110
1189
  const validation = validateToolInput(deleteMemberSchema, rawInput, 'ghost_delete_member');
1111
1190
  if (!validation.success) {
@@ -1141,10 +1220,13 @@ server.tool(
1141
1220
  );
1142
1221
 
1143
1222
  // Get Members Tool
1144
- server.tool(
1223
+ server.registerTool(
1145
1224
  'ghost_get_members',
1146
- 'Retrieves a list of members (subscribers) from Ghost CMS with optional filtering, pagination, and includes.',
1147
- getMembersSchema,
1225
+ {
1226
+ description:
1227
+ 'Retrieves a list of members (subscribers) from Ghost CMS with optional filtering, pagination, and includes.',
1228
+ inputSchema: getMembersSchema,
1229
+ },
1148
1230
  async (rawInput) => {
1149
1231
  const validation = validateToolInput(getMembersSchema, rawInput, 'ghost_get_members');
1150
1232
  if (!validation.success) {
@@ -1187,10 +1269,13 @@ server.tool(
1187
1269
  );
1188
1270
 
1189
1271
  // Get Member Tool
1190
- server.tool(
1272
+ server.registerTool(
1191
1273
  'ghost_get_member',
1192
- 'Retrieves a single member from Ghost CMS by ID or email. Provide either id OR email.',
1193
- getMemberSchema,
1274
+ {
1275
+ description:
1276
+ 'Retrieves a single member from Ghost CMS by ID or email. Provide either id OR email.',
1277
+ inputSchema: getMemberSchema,
1278
+ },
1194
1279
  async (rawInput) => {
1195
1280
  const validation = validateToolInput(getMemberSchema, rawInput, 'ghost_get_member');
1196
1281
  if (!validation.success) {
@@ -1226,10 +1311,12 @@ server.tool(
1226
1311
  );
1227
1312
 
1228
1313
  // Search Members Tool
1229
- server.tool(
1314
+ server.registerTool(
1230
1315
  'ghost_search_members',
1231
- 'Searches for members by name or email in Ghost CMS.',
1232
- searchMembersSchema,
1316
+ {
1317
+ description: 'Searches for members by name or email in Ghost CMS.',
1318
+ inputSchema: searchMembersSchema,
1319
+ },
1233
1320
  async (rawInput) => {
1234
1321
  const validation = validateToolInput(searchMembersSchema, rawInput, 'ghost_search_members');
1235
1322
  if (!validation.success) {
@@ -1277,10 +1364,12 @@ const updateNewsletterInputSchema = z.object({ id: ghostIdSchema }).merge(update
1277
1364
  const deleteNewsletterSchema = z.object({ id: ghostIdSchema });
1278
1365
 
1279
1366
  // Get Newsletters Tool
1280
- server.tool(
1367
+ server.registerTool(
1281
1368
  'ghost_get_newsletters',
1282
- 'Retrieves a list of newsletters from Ghost CMS with optional filtering.',
1283
- newsletterQuerySchema,
1369
+ {
1370
+ description: 'Retrieves a list of newsletters from Ghost CMS with optional filtering.',
1371
+ inputSchema: newsletterQuerySchema,
1372
+ },
1284
1373
  async (rawInput) => {
1285
1374
  const validation = validateToolInput(newsletterQuerySchema, rawInput, 'ghost_get_newsletters');
1286
1375
  if (!validation.success) {
@@ -1322,10 +1411,12 @@ server.tool(
1322
1411
  );
1323
1412
 
1324
1413
  // Get Newsletter Tool
1325
- server.tool(
1414
+ server.registerTool(
1326
1415
  'ghost_get_newsletter',
1327
- 'Retrieves a single newsletter from Ghost CMS by ID.',
1328
- getNewsletterSchema,
1416
+ {
1417
+ description: 'Retrieves a single newsletter from Ghost CMS by ID.',
1418
+ inputSchema: getNewsletterSchema,
1419
+ },
1329
1420
  async (rawInput) => {
1330
1421
  const validation = validateToolInput(getNewsletterSchema, rawInput, 'ghost_get_newsletter');
1331
1422
  if (!validation.success) {
@@ -1361,10 +1452,13 @@ server.tool(
1361
1452
  );
1362
1453
 
1363
1454
  // Create Newsletter Tool
1364
- server.tool(
1455
+ server.registerTool(
1365
1456
  'ghost_create_newsletter',
1366
- 'Creates a new newsletter in Ghost CMS with customizable sender settings and display options.',
1367
- createNewsletterSchema,
1457
+ {
1458
+ description:
1459
+ 'Creates a new newsletter in Ghost CMS with customizable sender settings and display options.',
1460
+ inputSchema: createNewsletterSchema,
1461
+ },
1368
1462
  async (rawInput) => {
1369
1463
  const validation = validateToolInput(
1370
1464
  createNewsletterSchema,
@@ -1404,10 +1498,13 @@ server.tool(
1404
1498
  );
1405
1499
 
1406
1500
  // Update Newsletter Tool
1407
- server.tool(
1501
+ server.registerTool(
1408
1502
  'ghost_update_newsletter',
1409
- 'Updates an existing newsletter in Ghost CMS. Can update name, description, sender settings, and display options.',
1410
- updateNewsletterInputSchema,
1503
+ {
1504
+ description:
1505
+ 'Updates an existing newsletter in Ghost CMS. Can update name, description, sender settings, and display options.',
1506
+ inputSchema: updateNewsletterInputSchema,
1507
+ },
1411
1508
  async (rawInput) => {
1412
1509
  const validation = validateToolInput(
1413
1510
  updateNewsletterInputSchema,
@@ -1449,10 +1546,13 @@ server.tool(
1449
1546
  );
1450
1547
 
1451
1548
  // Delete Newsletter Tool
1452
- server.tool(
1549
+ server.registerTool(
1453
1550
  'ghost_delete_newsletter',
1454
- 'Deletes a newsletter from Ghost CMS by ID. This operation is permanent and cannot be undone.',
1455
- deleteNewsletterSchema,
1551
+ {
1552
+ description:
1553
+ 'Deletes a newsletter from Ghost CMS by ID. This operation is permanent and cannot be undone.',
1554
+ inputSchema: deleteNewsletterSchema,
1555
+ },
1456
1556
  async (rawInput) => {
1457
1557
  const validation = validateToolInput(
1458
1558
  deleteNewsletterSchema,
@@ -1499,10 +1599,13 @@ const updateTierInputSchema = z.object({ id: ghostIdSchema }).merge(updateTierSc
1499
1599
  const deleteTierSchema = z.object({ id: ghostIdSchema });
1500
1600
 
1501
1601
  // Get Tiers Tool
1502
- server.tool(
1602
+ server.registerTool(
1503
1603
  'ghost_get_tiers',
1504
- 'Retrieves a list of tiers (membership levels) from Ghost CMS with optional filtering by type (free/paid).',
1505
- tierQuerySchema,
1604
+ {
1605
+ description:
1606
+ 'Retrieves a list of tiers (membership levels) from Ghost CMS with optional filtering by type (free/paid).',
1607
+ inputSchema: tierQuerySchema,
1608
+ },
1506
1609
  async (rawInput) => {
1507
1610
  const validation = validateToolInput(tierQuerySchema, rawInput, 'ghost_get_tiers');
1508
1611
  if (!validation.success) {
@@ -1538,10 +1641,12 @@ server.tool(
1538
1641
  );
1539
1642
 
1540
1643
  // Get Tier Tool
1541
- server.tool(
1644
+ server.registerTool(
1542
1645
  'ghost_get_tier',
1543
- 'Retrieves a single tier (membership level) from Ghost CMS by ID.',
1544
- getTierSchema,
1646
+ {
1647
+ description: 'Retrieves a single tier (membership level) from Ghost CMS by ID.',
1648
+ inputSchema: getTierSchema,
1649
+ },
1545
1650
  async (rawInput) => {
1546
1651
  const validation = validateToolInput(getTierSchema, rawInput, 'ghost_get_tier');
1547
1652
  if (!validation.success) {
@@ -1577,10 +1682,12 @@ server.tool(
1577
1682
  );
1578
1683
 
1579
1684
  // Create Tier Tool
1580
- server.tool(
1685
+ server.registerTool(
1581
1686
  'ghost_create_tier',
1582
- 'Creates a new tier (membership level) in Ghost CMS with pricing and benefits.',
1583
- createTierSchema,
1687
+ {
1688
+ description: 'Creates a new tier (membership level) in Ghost CMS with pricing and benefits.',
1689
+ inputSchema: createTierSchema,
1690
+ },
1584
1691
  async (rawInput) => {
1585
1692
  const validation = validateToolInput(createTierSchema, rawInput, 'ghost_create_tier');
1586
1693
  if (!validation.success) {
@@ -1616,10 +1723,13 @@ server.tool(
1616
1723
  );
1617
1724
 
1618
1725
  // Update Tier Tool
1619
- server.tool(
1726
+ server.registerTool(
1620
1727
  'ghost_update_tier',
1621
- 'Updates an existing tier (membership level) in Ghost CMS. Can update pricing, benefits, and other tier properties.',
1622
- updateTierInputSchema,
1728
+ {
1729
+ description:
1730
+ 'Updates an existing tier (membership level) in Ghost CMS. Can update pricing, benefits, and other tier properties.',
1731
+ inputSchema: updateTierInputSchema,
1732
+ },
1623
1733
  async (rawInput) => {
1624
1734
  const validation = validateToolInput(updateTierInputSchema, rawInput, 'ghost_update_tier');
1625
1735
  if (!validation.success) {
@@ -1657,10 +1767,13 @@ server.tool(
1657
1767
  );
1658
1768
 
1659
1769
  // Delete Tier Tool
1660
- server.tool(
1770
+ server.registerTool(
1661
1771
  'ghost_delete_tier',
1662
- 'Deletes a tier (membership level) from Ghost CMS by ID. This operation is permanent and cannot be undone.',
1663
- deleteTierSchema,
1772
+ {
1773
+ description:
1774
+ 'Deletes a tier (membership level) from Ghost CMS by ID. This operation is permanent and cannot be undone.',
1775
+ inputSchema: deleteTierSchema,
1776
+ },
1664
1777
  async (rawInput) => {
1665
1778
  const validation = validateToolInput(deleteTierSchema, rawInput, 'ghost_delete_tier');
1666
1779
  if (!validation.success) {
@@ -296,7 +296,7 @@ class ResourceFetcher {
296
296
  }
297
297
 
298
298
  case 'name': {
299
- const tagsByName = await this.ghostService.getTags(identifier);
299
+ const tagsByName = await this.ghostService.getTags({ filter: `name:'${identifier}'` });
300
300
  tag = tagsByName[0];
301
301
  break;
302
302
  }
@@ -329,7 +329,11 @@ class ResourceFetcher {
329
329
  }
330
330
 
331
331
  // Fetch from Ghost
332
- const tags = await this.ghostService.getTags(query.name);
332
+ const options = {};
333
+ if (query.name) {
334
+ options.filter = `name:'${query.name}'`;
335
+ }
336
+ const tags = await this.ghostService.getTags(options);
333
337
 
334
338
  // Apply client-side filtering if needed
335
339
  let filteredTags = tags;
@@ -459,7 +459,7 @@ describe('ResourceManager', () => {
459
459
  const result = await resourceManager.fetchResource('ghost/tag/name:Technology');
460
460
 
461
461
  expect(result).toEqual(tags[0]);
462
- expect(mockGhostService.getTags).toHaveBeenCalledWith('Technology');
462
+ expect(mockGhostService.getTags).toHaveBeenCalledWith({ filter: "name:'Technology'" });
463
463
  });
464
464
 
465
465
  it('should use id by default when no identifier type specified', async () => {
@@ -511,7 +511,7 @@ describe('ResourceManager', () => {
511
511
 
512
512
  await resourceManager.fetchResource('ghost/tags?name=Tech');
513
513
 
514
- expect(mockGhostService.getTags).toHaveBeenCalledWith('Tech');
514
+ expect(mockGhostService.getTags).toHaveBeenCalledWith({ filter: "name:'Tech'" });
515
515
  });
516
516
 
517
517
  it('should apply client-side filtering', async () => {