ghost-mcp/contracts/content-api-auth.md

6.4 KiB

Ghost Content API Authentication Documentation

Overview

Ghost Content API provides read-only access to published content using simple query parameter authentication. It's designed for public consumption and is safe for client-side use.

Authentication Method

API Key Format

  • Type: Content API Key (different from Admin API Key)
  • Source: Ghost Admin → Settings → Integrations → Custom Integration
  • Security: Safe for browser/public environments
  • Access: Read-only to published content only

Request Authentication

Query Parameter Method

GET /ghost/api/content/posts/?key={content_api_key}

Required Headers

Accept-Version: v5.0

Example Request

GET /ghost/api/content/posts/?key=22444f78cc7a3e8d0b5eaa18&limit=5 HTTP/1.1
Host: localhost:2368
Accept-Version: v5.0

Implementation for MCP Server

Node.js Implementation

Simple Request Function

class GhostContentAuth {
  constructor(apiKey, ghostUrl = 'http://localhost:2368') {
    this.apiKey = apiKey;
    this.baseUrl = `${ghostUrl}/ghost/api/content`;
  }

  buildUrl(endpoint, params = {}) {
    const url = new URL(`${this.baseUrl}${endpoint}`);

    // Add API key
    url.searchParams.set('key', this.apiKey);

    // Add additional parameters
    Object.entries(params).forEach(([key, value]) => {
      if (value !== undefined && value !== null) {
        url.searchParams.set(key, value);
      }
    });

    return url.toString();
  }

  getHeaders() {
    return {
      'Accept-Version': 'v5.0',
      'Accept': 'application/json'
    };
  }

  async request(endpoint, params = {}) {
    const url = this.buildUrl(endpoint, params);
    const response = await fetch(url, {
      headers: this.getHeaders()
    });

    if (!response.ok) {
      throw new Error(`Content API request failed: ${response.status}`);
    }

    return response.json();
  }
}

Usage Examples

const contentAuth = new GhostContentAuth('your_content_api_key');

// Get all posts
const posts = await contentAuth.request('/posts/', {
  limit: 10,
  include: 'tags,authors'
});

// Get specific post by slug
const post = await contentAuth.request('/posts/slug/welcome/', {
  include: 'tags,authors'
});

// Get site settings
const settings = await contentAuth.request('/settings/');

Available Endpoints

Core Resources

  • GET /posts/ - All published posts
  • GET /posts/{id}/ - Specific post by ID
  • GET /posts/slug/{slug}/ - Specific post by slug
  • GET /pages/ - All published pages
  • GET /pages/{id}/ - Specific page by ID
  • GET /pages/slug/{slug}/ - Specific page by slug
  • GET /tags/ - All public tags
  • GET /tags/{id}/ - Specific tag by ID
  • GET /tags/slug/{slug}/ - Specific tag by slug
  • GET /authors/ - All authors
  • GET /authors/{id}/ - Specific author by ID
  • GET /authors/slug/{slug}/ - Specific author by slug
  • GET /tiers/ - Membership tiers
  • GET /settings/ - Public site settings

Query Parameters

Common Parameters

  • key (required): Content API key
  • limit: Number of resources (default: 15, max: 50)
  • page: Page number for pagination
  • fields: Comma-separated list of fields to include
  • include: Related resources to include (e.g., 'tags,authors')
  • filter: Filter resources using Ghost's filter syntax

Filter Examples

// Featured posts only
const featured = await contentAuth.request('/posts/', {
  filter: 'featured:true'
});

// Posts with specific tag
const newsPosts = await contentAuth.request('/posts/', {
  filter: 'tag:news'
});

// Posts by specific author
const authorPosts = await contentAuth.request('/posts/', {
  filter: 'author:john-doe'
});

Response Format

Standard Response Structure

{
  "posts": [
    {
      "id": "post_id",
      "title": "Post Title",
      "slug": "post-slug",
      "html": "<p>Post content...</p>",
      "feature_image": "image_url",
      "published_at": "2023-01-01T00:00:00.000Z",
      "created_at": "2023-01-01T00:00:00.000Z",
      "updated_at": "2023-01-01T00:00:00.000Z",
      "tags": [...],
      "authors": [...]
    }
  ],
  "meta": {
    "pagination": {
      "page": 1,
      "limit": 15,
      "pages": 1,
      "total": 1,
      "next": null,
      "prev": null
    }
  }
}

Error Handling

Missing API Key

Request: Without key parameter Response: 401 Unauthorized

{
  "errors": [
    {
      "message": "Authorization failed",
      "context": "Unable to determine the authenticated member or integration. Check the supplied Content API Key...",
      "type": "UnauthorizedError"
    }
  ]
}

Invalid API Key

Response: 401 Unauthorized Action: Verify key is correct and active

Resource Not Found

Response: 404 Not Found

{
  "errors": [
    {
      "message": "Resource not found",
      "type": "NotFoundError"
    }
  ]
}

Security Considerations

Safe for Public Use

  • Content API keys only access published content
  • No sensitive data exposure
  • Can be used in browsers, mobile apps, etc.

Private Sites

  • Be cautious with key distribution on private Ghost sites
  • Consider access restrictions if content should be limited

Key Management

  • Keys can be regenerated in Ghost Admin
  • Multiple integrations can have different keys
  • Monitor key usage if needed

Rate Limiting & Caching

Caching Behavior

  • Content API responses are fully cacheable
  • Cache headers provided in responses
  • Recommended to implement caching in client

Rate Limiting

  • No strict rate limits documented
  • Reasonable usage expected
  • Monitor response times and adjust accordingly

Testing Strategy

Manual Testing

# Test with curl
curl "http://localhost:2368/ghost/api/content/posts/?key=YOUR_KEY&limit=1" \
  -H "Accept-Version: v5.0"

Automated Testing

// Test basic authentication
test('Content API authentication', async () => {
  const response = await contentAuth.request('/settings/');
  expect(response.settings).toBeDefined();
});

// Test error handling
test('Invalid key handling', async () => {
  const invalidAuth = new GhostContentAuth('invalid_key');
  await expect(invalidAuth.request('/posts/')).rejects.toThrow();
});

Implementation Status

  • Authentication method documented
  • Request format identified
  • Error handling patterns documented
  • Node.js implementation designed
  • Implementation pending API key generation
  • Testing pending API key availability