mcpField
G
ghost-blog

ghost-blog

github.com/hoangvantuanMarketing & growth038 installsUpdated 2w ago
Repository

About ghost-blog

ghost-blog is a set of scripts for managing Ghost posts and tags through the Ghost Admin API. It is for Ghost blog operators and supports both single-item and bulk actions, with bulk previews by default and confirmation required for deletion.

Key features

  1. 01Filter and search posts by status or tag
  2. 02Create, read, update, publish, and unpublish posts
  3. 03Bulk publish, unpublish, and tag posts
  4. 04List, create, and delete tags
  5. 05Preview bulk actions by default; confirm deletions

Use cases

  1. 01Browse and filter draft or published posts
  2. 02Create posts and publish or unpublish them
  3. 03Organize tags across multiple posts
  4. 04Preview bulk changes before execution

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/hoangvantuan/claude-plugin --skill ghost-blog

After installing, type/ghost-blogin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

scripts/
.env.example
.gitignore
ghost_core.py
newsletters_manage.py
posts_browse.py
posts_bulk.py
posts_crud.py
requirements.txt
tags_manage.py
test_ghost_core.py
test_posts_browse.py

Details

From the project README

Ghost Blog Management

Manage Ghost CMS posts and tags via Admin API.

Setup

1. Install Dependencies
cd .claude/skills/ghost-blog/scripts
uv venv
uv pip install -r requirements.txt
2. Configure Environment
cd .claude/skills/ghost-blog/scripts
cp .env.example .env
# Edit .env with your Ghost credentials

.env file:

GHOST_URL=https://your-blog.ghost.io
GHOST_ADMIN_KEY=your-key-id:your-secret-key
GHOST_API_VERSION=v5.0

Get your Admin API Key:

  1. Go to Ghost Admin → Settings → Integrations
  2. Create a Custom Integration
  3. Copy the Admin API Key (format: id:secret)
3. Verify Setup
cd .claude/skills/ghost-blog/scripts && uv run python ghost_core.py

Quick Start

List posts:

python scripts/posts_browse.py --status draft
python scripts/posts_browse.py --tag news --featured

Manage single post:

python scripts/posts_crud.py get --id POST_ID
python scripts/posts_crud.py create --title "New Post" --html "<p>Content</p>"
python scripts/posts_crud.py publish --id POST_ID

Bulk operations:

python scripts/posts_bulk.py publish --filter "status:draft" --execute
python scripts/posts_bulk.py add-tag --filter "status:published" --tag "archive" --execute

Manage tags:

python scripts/tags_manage.py list
python scripts/tags_manage.py create --name "Tutorial"

Dispatch Rules

User IntentScriptExample Command
test, run testspytestcd scripts && uv run pytest -v
list posts, show drafts, filter postsposts_browse.py--status draft --tag news
get post, read post, show postposts_crud.py get--id xxx or --slug xxx
create post, new post, write postposts_crud.py create--title "..." --html "..."
update post, edit post, change postposts_crud.py update--id xxx --title "..."
delete post, remove postposts_crud.py delete--id xxx --confirm
publish post, publish draftposts_crud.py publish--id xxx
unpublish postposts_crud.py unpublish--id xxx
bulk publish, publish all draftsposts_bulk.py publish--filter "..." --execute
bulk unpublishposts_bulk.py unpublish--filter "..." --execute
add tag to posts, tag postsposts_bulk.py add-tag--filter "..." --tag xxx --execute
remove tag from postsposts_bulk.py remove-tag--filter "..." --tag xxx --execute
list tags, show tagstags_manage.py list(no options needed)
create tag, new tagtags_manage.py create--name "..."
delete tag, remove tagtags_manage.py delete--slug xxx --confirm

Scripts

ScriptPurpose
ghost_core.pyShared: JWT auth, HTTP client, error handling
posts_browse.pyList, filter, search posts
posts_crud.pyCRUD operations for single posts
posts_bulk.pyBatch operations (publish, tags, featured)
tags_manage.pyCRUD operations for tags

Filter Syntax (NQL)

Ghost uses NQL for filtering:

# Status
status:draft
status:published
status:scheduled

# Tags (use slug, not name)
tag:news
tags:[news,tutorial]

# Featured
featured:true

# Combine (AND)
status:published+featured:true

# Combine (OR)
status:draft,status:scheduled

Note: The --tag option in posts_browse.py accepts both tag names and slugs. It automatically resolves names to slugs via API lookup.

Creating Posts from Markdown Files

When creating posts from markdown files (e.g., a blog series):

import markdown
from ghost_core import api_request

# 1. Convert Markdown to HTML
md_content = open('article.md').read()
html_content = markdown.markdown(md_content, extensions=['extra', 'nl2br'])

# 2. Create post with source=html (CRITICAL for Ghost 5.0+)
post_data = {
    'title': 'My Post',
    'html': html_content,
    'status': 'draft',
    'tags': ['my-tag']
}
response = api_request('POST', 'posts/',
                       data={'posts': [post_data]},
                       params={'source': 'html'})  # Required!

Important: Ghost 5.0+ uses Lexical editor format. The source=html param tells Ghost to convert HTML to Lexical. Without it, post content will be empty!

Fixing Internal Links

If markdown files have internal links like [Title](other-file.md), replace them with Ghost slugs after creating posts:

LINK_MAP = {
    'old-file.md': '/new-ghost-slug/',
}

html = html.replace('href="old-file.md"', 'href="/new-ghost-slug/"')

Safety Features

  • Bulk operations: Preview mode by default

  • Delete operations: Require --confirm flag

  • API versioning: Uses Ghost v5.0 API

  • HTML conversion: Auto-adds source=html param for Ghost 5.0+ compatibility

Testing

Setup Test Environment
cd .claude/skills/ghost-blog/scripts
uv venv
uv pip install -r requirements.txt
Run Tests
cd .claude/skills/ghost-blog/scripts && uv run pytest -v
Run with Coverage
cd .claude/skills/ghost-blog/scripts && uv run pytest -v --cov=. --cov-report=term-missing

Troubleshooting

ErrorCauseSolution
"No virtual environment found"Missing venvRun uv venv in scripts directory
"Failed to spawn: pytest"Missing depsRun uv pip install -r requirements.txt
"GHOST_URL not set"Missing .envCopy .env.example to .env and configure
"GHOST_ADMIN_KEY invalid format"Wrong key formatKey must be id:secret format
"UnauthorizedError"Invalid API keyCheck key is valid and has permissions
"UPDATE_COLLISION"Post modifiedRetry operation (auto-refetches updated_at)
Post content emptyMissing source=htmlGhost 5.0+ requires source=html param
Tag filter returns 0Using name instead of slugUse tag slug or let script resolve it
HTTP 422 on tags listInvalid paramsDon't use include=count.posts or order
Links point to .md filesInternal markdown linksReplace with Ghost slugs after creating posts

References

Comments

Markdown supported