@jgardner04/ghost-mcp-server 1.12.3 → 1.12.5

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.3",
3
+ "version": "1.12.5",
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",
@@ -73,7 +73,8 @@
73
73
  "sharp": "^0.34.1",
74
74
  "uuid": "^11.1.0",
75
75
  "winston": "^3.17.0",
76
- "zod": "^3.25.76"
76
+ "zod": "^3.25.76",
77
+ "zod-to-json-schema": "^3.25.0"
77
78
  },
78
79
  "keywords": [
79
80
  "ghost",
@@ -439,7 +439,7 @@ export async function retryWithBackoff(fn, options = {}) {
439
439
  }
440
440
 
441
441
  const delay = ErrorHandler.getRetryDelay(attempt, error);
442
- console.log(`Retry attempt ${attempt}/${maxAttempts} after ${delay}ms`);
442
+ console.error(`Retry attempt ${attempt}/${maxAttempts} after ${delay}ms`);
443
443
 
444
444
  await new Promise((resolve) => setTimeout(resolve, delay));
445
445
  onRetry(attempt, error);
@@ -24,7 +24,7 @@ import {
24
24
 
25
25
  dotenv.config();
26
26
 
27
- console.log('Initializing Enhanced MCP Server...');
27
+ console.error('Initializing Enhanced MCP Server...');
28
28
 
29
29
  // Initialize components
30
30
  const resourceManager = new ResourceManager(ghostService);
@@ -48,7 +48,7 @@ const mcpServer = new MCPServer({
48
48
 
49
49
  // --- Register Resources with Enhanced Fetching ---
50
50
 
51
- console.log('Registering enhanced resources...');
51
+ console.error('Registering enhanced resources...');
52
52
 
53
53
  // Ghost Post Resource
54
54
  const postResource = resourceManager.registerResource(
@@ -127,7 +127,7 @@ mcpServer.addResource(tagResource);
127
127
 
128
128
  // --- Enhanced Tools ---
129
129
 
130
- console.log('Registering enhanced tools...');
130
+ console.error('Registering enhanced tools...');
131
131
 
132
132
  // Batch Operations Tool
133
133
  const batchOperationsTool = new Tool({
@@ -434,7 +434,7 @@ const subscriptionTool = new Tool({
434
434
  input.uri,
435
435
  (event) => {
436
436
  // In a real implementation, this would send events to the client
437
- console.log('Resource update:', event);
437
+ console.error('Resource update:', event);
438
438
  },
439
439
  input.options || {}
440
440
  );
@@ -468,13 +468,13 @@ mcpServer.addTool(subscriptionTool);
468
468
 
469
469
  const startEnhancedMCPServer = async (transport = 'http', options = {}) => {
470
470
  try {
471
- console.log(`Starting Enhanced MCP Server with ${transport} transport...`);
471
+ console.error(`Starting Enhanced MCP Server with ${transport} transport...`);
472
472
 
473
473
  switch (transport) {
474
474
  case 'stdio': {
475
475
  const stdioTransport = new StdioServerTransport();
476
476
  await mcpServer.connect(stdioTransport);
477
- console.log('Enhanced MCP Server running on stdio transport');
477
+ console.error('Enhanced MCP Server running on stdio transport');
478
478
  break;
479
479
  }
480
480
 
@@ -553,10 +553,10 @@ const startEnhancedMCPServer = async (transport = 'http', options = {}) => {
553
553
  await mcpServer.connect(sseTransport);
554
554
 
555
555
  const server = app.listen(port, () => {
556
- console.log(`Enhanced MCP Server (SSE) listening on port ${port}`);
557
- console.log(`Health: http://localhost:${port}/health`);
558
- console.log(`Resources: http://localhost:${port}/resources`);
559
- console.log(`SSE: http://localhost:${port}/mcp/sse`);
556
+ console.error(`Enhanced MCP Server (SSE) listening on port ${port}`);
557
+ console.error(`Health: http://localhost:${port}/health`);
558
+ console.error(`Resources: http://localhost:${port}/resources`);
559
+ console.error(`SSE: http://localhost:${port}/mcp/sse`);
560
560
  });
561
561
 
562
562
  mcpServer._httpServer = server;
@@ -574,7 +574,7 @@ const startEnhancedMCPServer = async (transport = 'http', options = {}) => {
574
574
  const wss = new WebSocketServer({ port: wsPort });
575
575
 
576
576
  wss.on('connection', async (ws) => {
577
- console.log('New WebSocket connection');
577
+ console.error('New WebSocket connection');
578
578
 
579
579
  const wsTransport = new WebSocketServerTransport(ws);
580
580
  await mcpServer.connect(wsTransport);
@@ -616,7 +616,7 @@ const startEnhancedMCPServer = async (transport = 'http', options = {}) => {
616
616
  });
617
617
  });
618
618
 
619
- console.log(`Enhanced MCP Server (WebSocket) listening on port ${wsPort}`);
619
+ console.error(`Enhanced MCP Server (WebSocket) listening on port ${wsPort}`);
620
620
  mcpServer._wss = wss;
621
621
  break;
622
622
  }
@@ -626,18 +626,18 @@ const startEnhancedMCPServer = async (transport = 'http', options = {}) => {
626
626
  }
627
627
 
628
628
  // Log capabilities
629
- console.log('Server Capabilities:');
630
- console.log(
629
+ console.error('Server Capabilities:');
630
+ console.error(
631
631
  '- Resources:',
632
632
  mcpServer.listResources().map((r) => r.name)
633
633
  );
634
- console.log(
634
+ console.error(
635
635
  '- Tools:',
636
636
  mcpServer.listTools().map((t) => t.name)
637
637
  );
638
- console.log('- Cache enabled with LRU eviction');
639
- console.log('- Subscription support for real-time updates');
640
- console.log('- Batch operations for efficiency');
638
+ console.error('- Cache enabled with LRU eviction');
639
+ console.error('- Subscription support for real-time updates');
640
+ console.error('- Batch operations for efficiency');
641
641
  } catch (error) {
642
642
  errorLogger.logError(error);
643
643
  console.error('Failed to start Enhanced MCP Server:', error);
@@ -647,7 +647,7 @@ const startEnhancedMCPServer = async (transport = 'http', options = {}) => {
647
647
 
648
648
  // Graceful shutdown
649
649
  const shutdown = async () => {
650
- console.log('\nShutting down Enhanced MCP Server...');
650
+ console.error('\nShutting down Enhanced MCP Server...');
651
651
 
652
652
  // Clear all subscriptions
653
653
  resourceManager.subscriptionManager.subscriptions.clear();
@@ -299,7 +299,7 @@ describe('errorMiddleware', () => {
299
299
 
300
300
  describe('logInfo', () => {
301
301
  it('should log info message when level allows', async () => {
302
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
302
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
303
303
 
304
304
  const logger = new ErrorLogger({ logLevel: 'info', enableFileLogging: false });
305
305
  await logger.logInfo('Info message');
@@ -309,7 +309,7 @@ describe('errorMiddleware', () => {
309
309
  });
310
310
 
311
311
  it('should not log when level is higher', async () => {
312
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
312
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
313
313
 
314
314
  const logger = new ErrorLogger({ logLevel: 'error', enableFileLogging: false });
315
315
  await logger.logInfo('Info message');
@@ -333,7 +333,7 @@ describe('errorMiddleware', () => {
333
333
 
334
334
  describe('logDebug', () => {
335
335
  it('should log debug message when level is debug', async () => {
336
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
336
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
337
337
 
338
338
  const logger = new ErrorLogger({ logLevel: 'debug', enableFileLogging: false });
339
339
  await logger.logDebug('Debug message');
@@ -343,7 +343,7 @@ describe('errorMiddleware', () => {
343
343
  });
344
344
 
345
345
  it('should not log when level is not debug', async () => {
346
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
346
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
347
347
 
348
348
  const logger = new ErrorLogger({ logLevel: 'info', enableFileLogging: false });
349
349
  await logger.logDebug('Debug message');
@@ -111,7 +111,7 @@ export class ErrorLogger {
111
111
 
112
112
  async logInfo(message, meta = {}) {
113
113
  if (['info', 'debug'].includes(this.logLevel)) {
114
- console.log(`[INFO] ${message}`);
114
+ console.error(`[INFO] ${message}`);
115
115
  const entry = this.formatLogEntry('info', message, meta);
116
116
  await this.writeToFile('app', entry);
117
117
  }
@@ -127,7 +127,7 @@ export class ErrorLogger {
127
127
 
128
128
  async logDebug(message, meta = {}) {
129
129
  if (this.logLevel === 'debug') {
130
- console.log(`[DEBUG] ${message}`);
130
+ console.error(`[DEBUG] ${message}`);
131
131
  const entry = this.formatLogEntry('debug', message, meta);
132
132
  await this.writeToFile('debug', entry);
133
133
  }
@@ -446,11 +446,11 @@ export class GracefulShutdown {
446
446
  if (this.isShuttingDown) return;
447
447
 
448
448
  this.isShuttingDown = true;
449
- console.log('Graceful shutdown initiated...');
449
+ console.error('Graceful shutdown initiated...');
450
450
 
451
451
  // Stop accepting new connections
452
452
  server.close(() => {
453
- console.log('Server closed to new connections');
453
+ console.error('Server closed to new connections');
454
454
  });
455
455
 
456
456
  // Close existing connections
@@ -168,7 +168,7 @@ class ResourceFetcher {
168
168
  // Check cache
169
169
  const cached = this.cache.get(cacheKey);
170
170
  if (cached) {
171
- console.log(`Cache hit for ${cacheKey}`);
171
+ console.error(`Cache hit for ${cacheKey}`);
172
172
  return cached;
173
173
  }
174
174
 
@@ -221,7 +221,7 @@ class ResourceFetcher {
221
221
  // Check cache
222
222
  const cached = this.cache.get(cacheKey);
223
223
  if (cached) {
224
- console.log(`Cache hit for posts query`);
224
+ console.error(`Cache hit for posts query`);
225
225
  return cached;
226
226
  }
227
227
 
@@ -277,7 +277,7 @@ class ResourceFetcher {
277
277
  // Check cache
278
278
  const cached = this.cache.get(cacheKey);
279
279
  if (cached) {
280
- console.log(`Cache hit for ${cacheKey}`);
280
+ console.error(`Cache hit for ${cacheKey}`);
281
281
  return cached;
282
282
  }
283
283
 
@@ -324,7 +324,7 @@ class ResourceFetcher {
324
324
  // Check cache
325
325
  const cached = this.cache.get(cacheKey);
326
326
  if (cached) {
327
- console.log(`Cache hit for tags query`);
327
+ console.error(`Cache hit for tags query`);
328
328
  return cached;
329
329
  }
330
330
 
@@ -400,7 +400,7 @@ class ResourceSubscriptionManager extends EventEmitter {
400
400
  this.startPolling(subscriptionId, pollingInterval);
401
401
  }
402
402
 
403
- console.log(`Created subscription ${subscriptionId} for ${uri}`);
403
+ console.error(`Created subscription ${subscriptionId} for ${uri}`);
404
404
 
405
405
  return subscriptionId;
406
406
  }
@@ -418,7 +418,7 @@ class ResourceSubscriptionManager extends EventEmitter {
418
418
  // Remove subscription
419
419
  this.subscriptions.delete(subscriptionId);
420
420
 
421
- console.log(`Removed subscription ${subscriptionId}`);
421
+ console.error(`Removed subscription ${subscriptionId}`);
422
422
  }
423
423
 
424
424
  startPolling(subscriptionId, interval) {
@@ -539,7 +539,7 @@ export class ResourceManager {
539
539
  try {
540
540
  const parsed = ResourceURIParser.parse(uri);
541
541
 
542
- console.log('Fetching resource:', { uri: uri.substring(0, 100), parsed });
542
+ console.error('Fetching resource:', { uri: uri.substring(0, 100), parsed });
543
543
 
544
544
  // Route to appropriate fetcher
545
545
  switch (parsed.resourceType) {
@@ -605,7 +605,7 @@ export class ResourceManager {
605
605
  */
606
606
  invalidateCache(pattern = null) {
607
607
  this.cache.invalidate(pattern);
608
- console.log(`Cache invalidated${pattern ? ` for pattern: ${pattern}` : ''}`);
608
+ console.error(`Cache invalidated${pattern ? ` for pattern: ${pattern}` : ''}`);
609
609
  }
610
610
 
611
611
  /**
@@ -836,7 +836,7 @@ describe('ResourceManager', () => {
836
836
  });
837
837
 
838
838
  it('should log invalidation', async () => {
839
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
839
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
840
840
 
841
841
  resourceManager.invalidateCache();
842
842
  expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Cache invalidated'));
@@ -54,7 +54,7 @@ const handleApiRequest = async (resource, action, data = {}, options = {}, confi
54
54
  // Main execution function
55
55
  const executeRequest = async () => {
56
56
  try {
57
- console.log(`Executing Ghost API request: ${operation}`);
57
+ console.error(`Executing Ghost API request: ${operation}`);
58
58
 
59
59
  let result;
60
60
 
@@ -78,7 +78,7 @@ const handleApiRequest = async (resource, action, data = {}, options = {}, confi
78
78
  result = await api[resource][action](data);
79
79
  }
80
80
 
81
- console.log(`Successfully executed Ghost API request: ${operation}`);
81
+ console.error(`Successfully executed Ghost API request: ${operation}`);
82
82
  return result;
83
83
  } catch (error) {
84
84
  // Transform Ghost API errors into our error types
@@ -96,12 +96,12 @@ const handleApiRequest = async (resource, action, data = {}, options = {}, confi
96
96
  return await retryWithBackoff(wrappedExecute, {
97
97
  maxAttempts: maxRetries,
98
98
  onRetry: (attempt, _error) => {
99
- console.log(`Retrying ${operation} (attempt ${attempt}/${maxRetries})`);
99
+ console.error(`Retrying ${operation} (attempt ${attempt}/${maxRetries})`);
100
100
 
101
101
  // Log circuit breaker state if relevant
102
102
  if (useCircuitBreaker) {
103
103
  const state = ghostCircuitBreaker.getState();
104
- console.log(`Circuit breaker state:`, state);
104
+ console.error(`Circuit breaker state:`, state);
105
105
  }
106
106
  },
107
107
  });