@damusix/ghost-mcp 0.2.1 → 0.4.0
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/README.md +61 -1
- package/bin/ghost-keys.sh +58 -0
- package/dist/index.mjs +1104 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,9 +62,58 @@ claude mcp add ghost \
|
|
|
62
62
|
|
|
63
63
|
## Tools
|
|
64
64
|
|
|
65
|
+
### `compose_post`
|
|
66
|
+
|
|
67
|
+
Create or update a post from structured Koenig content blocks. Prefer this over
|
|
68
|
+
pushing raw HTML or hand-writing Lexical — it produces clean, natively-editable
|
|
69
|
+
posts. Prose blocks (`paragraph`, `heading`, `list`, `quote`) accept inline
|
|
70
|
+
markdown (`**bold**`, `_italic_`, `` `code` ``, `[links](url)`); rich features
|
|
71
|
+
are cards (`callout`, `image`, `button`, `bookmark`, `codeblock`, `toggle`,
|
|
72
|
+
`gallery`, and more). Omit `id` to create; set `id` + `updated_at` to update.
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"title": "We shipped the editor",
|
|
77
|
+
"status": "draft",
|
|
78
|
+
"blocks": [
|
|
79
|
+
{ "type": "heading", "level": 2, "text": "What's new" },
|
|
80
|
+
{ "type": "paragraph", "text": "Try the **new** [editor](https://ghost.org)." },
|
|
81
|
+
{ "type": "list", "style": "bullet", "items": ["Clean blocks", "Native editing"] },
|
|
82
|
+
{ "type": "callout", "emoji": "🚀", "color": "green", "text": "Live now" },
|
|
83
|
+
{ "type": "button", "text": "Get started", "url": "https://ghost.org" }
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Each block becomes a native Lexical node, so the post stays granularly editable
|
|
89
|
+
in Ghost — not a single opaque HTML block. See
|
|
90
|
+
[docs/koenig-cards.md](docs/koenig-cards.md) for every block type and field.
|
|
91
|
+
|
|
92
|
+
For long posts, write the blocks to a JSON file (a bare `[...]` array or
|
|
93
|
+
`{ "blocks": [...] }`) and pass its **absolute** path as `blockFile` instead of
|
|
94
|
+
`blocks` — useful when iterating, so you edit the file rather than re-sending the
|
|
95
|
+
whole array each time. Validate the file first with `compose_lexical`.
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{ "title": "Long post", "status": "draft", "blockFile": "/abs/path/tmp/post.json" }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### `compose_lexical`
|
|
102
|
+
|
|
103
|
+
Compile the same `blocks` into a Lexical JSON string without creating a post —
|
|
104
|
+
useful for preview or feeding into `use_ghost_api` yourself.
|
|
105
|
+
|
|
106
|
+
### `koenig_help`
|
|
107
|
+
|
|
108
|
+
List all block types, or get the fields and a JSON example for one block.
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{ "block": "callout" }
|
|
112
|
+
```
|
|
113
|
+
|
|
65
114
|
### `use_ghost_api`
|
|
66
115
|
|
|
67
|
-
Execute Ghost API actions. Supports full CRUD on posts, pages, tags, members, newsletters, offers, tiers, users, webhooks, images, themes, and site settings.
|
|
116
|
+
Execute Ghost API actions. Supports full CRUD on posts, pages, tags, members, newsletters, offers, tiers, users, webhooks, images, themes, and site settings. For post bodies, prefer `compose_post` over passing raw `lexical`/`html` here.
|
|
68
117
|
|
|
69
118
|
```json
|
|
70
119
|
{
|
|
@@ -109,6 +158,17 @@ Search Ghost documentation via `docs.ghost.org/llms.txt`.
|
|
|
109
158
|
- **Admin mode** (default): Full access to all Ghost Admin API and Content API actions. Requires `GHOST_ADMIN_API_KEY`.
|
|
110
159
|
- **Content mode**: Read-only access to Content API actions only. Requires `GHOST_CONTENT_API_KEY`. Admin actions are rejected with a clear error.
|
|
111
160
|
|
|
161
|
+
## Development
|
|
162
|
+
|
|
163
|
+
Spin up a throwaway Ghost 6 + MySQL 8 stack to exercise the server against a
|
|
164
|
+
live API, and inspect Ghost's real schema with noorm. See
|
|
165
|
+
[docs/experimentation.md](docs/experimentation.md).
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
docker compose up -d
|
|
169
|
+
bin/ghost-keys.sh --write # writes .env pointed at the local Ghost
|
|
170
|
+
```
|
|
171
|
+
|
|
112
172
|
## License
|
|
113
173
|
|
|
114
174
|
MIT
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Derive Ghost API keys for the MCP straight from the dockerized DB via noorm.
|
|
3
|
+
#
|
|
4
|
+
# Reads the admin + content keys for a Ghost integration and prints them as
|
|
5
|
+
# env lines (or writes them to .env with --write). The Admin API key is the
|
|
6
|
+
# Ghost `{key_id}:{secret}` form; the Content API key is the bare secret.
|
|
7
|
+
#
|
|
8
|
+
# bin/ghost-keys.sh # print keys for the "MCP Lab" integration
|
|
9
|
+
# bin/ghost-keys.sh "Zapier" # a different integration by name
|
|
10
|
+
# bin/ghost-keys.sh --write # write/refresh .env for the MCP
|
|
11
|
+
#
|
|
12
|
+
# Requires: the docker stack up (docker compose up -d) and noorm installed.
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
cd "$(dirname "$0")/.."
|
|
16
|
+
|
|
17
|
+
WRITE=0
|
|
18
|
+
INTEGRATION="MCP Lab"
|
|
19
|
+
for arg in "$@"; do
|
|
20
|
+
case "$arg" in
|
|
21
|
+
--write) WRITE=1 ;;
|
|
22
|
+
*) INTEGRATION="$arg" ;;
|
|
23
|
+
esac
|
|
24
|
+
done
|
|
25
|
+
|
|
26
|
+
# shellcheck disable=SC1091
|
|
27
|
+
set -a; source .noorm/dev.env; set +a
|
|
28
|
+
|
|
29
|
+
row() {
|
|
30
|
+
noorm --json sql "SELECT ak.secret AS secret, ak.id AS key_id FROM api_keys ak JOIN integrations i ON i.id = ak.integration_id WHERE i.name = '${INTEGRATION}' AND ak.type = '$1'" 2>/dev/null | grep '^{' | head -1
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
ADMIN_ROW=$(row admin)
|
|
34
|
+
CONTENT_ROW=$(row content)
|
|
35
|
+
|
|
36
|
+
if [[ -z "$ADMIN_ROW" ]]; then
|
|
37
|
+
echo "No admin key found for integration '${INTEGRATION}'." >&2
|
|
38
|
+
echo "Is the stack up, and does that integration exist? (Ghost Admin > Settings > Integrations)" >&2
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
ADMIN_KEY="$(jq -r '.key_id' <<<"$ADMIN_ROW"):$(jq -r '.secret' <<<"$ADMIN_ROW")"
|
|
43
|
+
CONTENT_KEY="$(jq -r '.secret' <<<"$CONTENT_ROW")"
|
|
44
|
+
|
|
45
|
+
if [[ "$WRITE" == "1" ]]; then
|
|
46
|
+
cat > .env <<EOF
|
|
47
|
+
# Local MCP -> dockerized Ghost (docker-compose.yml). Gitignored; throwaway creds.
|
|
48
|
+
# Regenerate with: bin/ghost-keys.sh --write
|
|
49
|
+
GHOST_URL=http://localhost:2368
|
|
50
|
+
GHOST_API_VERSION=v6.0
|
|
51
|
+
GHOST_ADMIN_API_KEY=${ADMIN_KEY}
|
|
52
|
+
GHOST_CONTENT_API_KEY=${CONTENT_KEY}
|
|
53
|
+
EOF
|
|
54
|
+
echo "Wrote .env for integration '${INTEGRATION}'."
|
|
55
|
+
else
|
|
56
|
+
echo "GHOST_ADMIN_API_KEY=${ADMIN_KEY}"
|
|
57
|
+
echo "GHOST_CONTENT_API_KEY=${CONTENT_KEY}"
|
|
58
|
+
fi
|