@jgardner04/ghost-mcp-server 1.12.1 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jgardner04/ghost-mcp-server",
3
- "version": "1.12.1",
3
+ "version": "1.12.2",
4
4
  "description": "A Model Context Protocol (MCP) server for interacting with Ghost CMS via the Admin API",
5
5
  "author": "Jonathan Gardner",
6
6
  "type": "module",
@@ -394,10 +394,11 @@ describe('mcp_server_improved - ghost_get_post tool', () => {
394
394
  expect(tool).toBeDefined();
395
395
  expect(tool.description).toContain('post');
396
396
  expect(tool.schema).toBeDefined();
397
- // Zod schemas store field definitions in schema.shape
398
- expect(tool.schema.shape.id).toBeDefined();
399
- expect(tool.schema.shape.slug).toBeDefined();
400
- expect(tool.schema.shape.include).toBeDefined();
397
+ // ghost_get_post uses a refined schema, access via _def.schema.shape
398
+ const shape = tool.schema._def.schema.shape;
399
+ expect(shape.id).toBeDefined();
400
+ expect(shape.slug).toBeDefined();
401
+ expect(shape.include).toBeDefined();
401
402
  });
402
403
 
403
404
  it('should retrieve post by ID', async () => {
@@ -1027,10 +1028,11 @@ describe('ghost_get_tag', () => {
1027
1028
 
1028
1029
  it('should have correct schema with id and slug as optional', () => {
1029
1030
  const tool = mockTools.get('ghost_get_tag');
1030
- // Zod schemas store field definitions in schema.shape
1031
- expect(tool.schema.shape.id).toBeDefined();
1032
- expect(tool.schema.shape.slug).toBeDefined();
1033
- expect(tool.schema.shape.include).toBeDefined();
1031
+ // ghost_get_tag uses a refined schema, access via _def.schema.shape
1032
+ const shape = tool.schema._def.schema.shape;
1033
+ expect(shape.id).toBeDefined();
1034
+ expect(shape.slug).toBeDefined();
1035
+ expect(shape.include).toBeDefined();
1034
1036
  });
1035
1037
 
1036
1038
  it('should retrieve tag by ID', async () => {
@@ -1089,7 +1091,7 @@ describe('ghost_get_tag', () => {
1089
1091
  const result = await tool.handler({});
1090
1092
 
1091
1093
  expect(result.content[0].type).toBe('text');
1092
- expect(result.content[0].text).toContain('Either id or slug must be provided');
1094
+ expect(result.content[0].text).toContain('Either id or slug is required');
1093
1095
  expect(result.isError).toBe(true);
1094
1096
  });
1095
1097
 
@@ -40,6 +40,13 @@ export class ValidationError extends BaseError {
40
40
  this.errors = errors;
41
41
  }
42
42
 
43
+ toJSON() {
44
+ return {
45
+ ...super.toJSON(),
46
+ ...(this.errors.length > 0 && { errors: this.errors }),
47
+ };
48
+ }
49
+
43
50
  static fromJoi(joiError) {
44
51
  const errors = joiError.details.map((detail) => ({
45
52
  field: detail.path.join('.'),
@@ -82,11 +82,18 @@ const server = new McpServer({
82
82
 
83
83
  // --- Schema Definitions for Tools ---
84
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
- });
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
+ });
90
97
  const updateTagInputSchema = updateTagSchema.extend({ id: ghostIdSchema });
91
98
  const deleteTagSchema = z.object({ id: ghostIdSchema });
92
99
 
@@ -187,10 +194,6 @@ server.tool(
187
194
 
188
195
  console.error(`Executing tool: ghost_get_tag`);
189
196
  try {
190
- if (!id && !slug) {
191
- throw new Error('Either id or slug must be provided');
192
- }
193
-
194
197
  await loadServices();
195
198
 
196
199
  // If slug is provided, use the slug/slug-name format
@@ -409,14 +412,18 @@ const getPostsSchema = postQuerySchema.extend({
409
412
  .optional()
410
413
  .describe('Filter posts by status. Options: published, draft, scheduled, all.'),
411
414
  });
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
- });
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
+ });
420
427
  const searchPostsSchema = z.object({
421
428
  query: z.string().min(1).describe('Search query to find in post titles.'),
422
429
  status: z
@@ -534,11 +541,6 @@ server.tool(
534
541
 
535
542
  console.error(`Executing tool: ghost_get_post`);
536
543
  try {
537
- // Validate that at least one of id or slug is provided
538
- if (!input.id && !input.slug) {
539
- throw new Error('Either id or slug is required to retrieve a post');
540
- }
541
-
542
544
  await loadServices();
543
545
 
544
546
  // Build options object