mcp-ghost-optimized 1.0.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/LICENSE +21 -0
- package/README.md +151 -0
- package/dist/api/auth.d.ts +19 -0
- package/dist/api/auth.d.ts.map +1 -0
- package/dist/api/auth.js +67 -0
- package/dist/api/auth.js.map +1 -0
- package/dist/api/client.d.ts +111 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +262 -0
- package/dist/api/client.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +378 -0
- package/dist/server.js.map +1 -0
- package/dist/utils/fields.d.ts +55 -0
- package/dist/utils/fields.d.ts.map +1 -0
- package/dist/utils/fields.js +101 -0
- package/dist/utils/fields.js.map +1 -0
- package/dist/utils/html.d.ts +26 -0
- package/dist/utils/html.d.ts.map +1 -0
- package/dist/utils/html.js +69 -0
- package/dist/utils/html.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Matthieu Cousin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# MCP Ghost Optimized
|
|
2
|
+
|
|
3
|
+
Serveur MCP pour Ghost CMS avec **field projection** pour réduire la consommation de tokens de 90-98%.
|
|
4
|
+
|
|
5
|
+
## Pourquoi ce MCP ?
|
|
6
|
+
|
|
7
|
+
Le MCP officiel `@fanyangmeng/ghost-mcp` retourne ~14K tokens par appel. Ce MCP optimisé retourne ~200-1500 tokens selon le mode choisi.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install
|
|
13
|
+
npm run build
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Configuration
|
|
17
|
+
|
|
18
|
+
Ajouter dans `.mcp.json` :
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"ghost-optimized": {
|
|
24
|
+
"command": "node",
|
|
25
|
+
"args": ["/chemin/vers/mcp-ghost-perso/dist/index.js"],
|
|
26
|
+
"env": {
|
|
27
|
+
"GHOST_URL": "https://votre-blog.ghost.io",
|
|
28
|
+
"GHOST_ADMIN_API_KEY": "id:secret",
|
|
29
|
+
"GHOST_DEFAULT_TEMPLATE": "custom-template-perso"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Variables d'environnement
|
|
37
|
+
|
|
38
|
+
| Variable | Obligatoire | Description |
|
|
39
|
+
|----------|-------------|-------------|
|
|
40
|
+
| `GHOST_URL` | Oui | URL de votre blog Ghost |
|
|
41
|
+
| `GHOST_ADMIN_API_KEY` | Oui | Clé API Admin (format `id:secret`) |
|
|
42
|
+
| `GHOST_DEFAULT_TEMPLATE` | Non | Template par défaut pour les nouveaux posts |
|
|
43
|
+
|
|
44
|
+
## Tools disponibles
|
|
45
|
+
|
|
46
|
+
### Posts
|
|
47
|
+
|
|
48
|
+
| Tool | Description |
|
|
49
|
+
|------|-------------|
|
|
50
|
+
| `posts_browse` | Liste les posts avec modes `minimal`, `compact`, `standard` |
|
|
51
|
+
| `posts_read` | Lit un post avec modes `metadata`, `content`, `full` |
|
|
52
|
+
| `posts_add` | Crée un post (supporte HTML via `?source=html`) |
|
|
53
|
+
| `posts_edit` | Modifie un post (gère auto `updated_at`) |
|
|
54
|
+
| `posts_delete` | Supprime un post (requiert `confirm: true` + `title`) |
|
|
55
|
+
|
|
56
|
+
### Tags
|
|
57
|
+
|
|
58
|
+
| Tool | Description |
|
|
59
|
+
|------|-------------|
|
|
60
|
+
| `tags_browse` | Liste les tags avec modes `minimal`, `standard` |
|
|
61
|
+
| `tags_read` | Lit un tag par ID ou slug |
|
|
62
|
+
|
|
63
|
+
### Images
|
|
64
|
+
|
|
65
|
+
| Tool | Description |
|
|
66
|
+
|------|-------------|
|
|
67
|
+
| `images_upload` | Upload une image depuis `file_path` ou `url` |
|
|
68
|
+
|
|
69
|
+
## Exemples d'utilisation
|
|
70
|
+
|
|
71
|
+
### Lister les posts publiés
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
posts_browse(status: "published", mode: "compact", limit: 10)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Filtrer par tag (NQL)
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
posts_browse(filter: "tag:finance+status:published")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Créer un post avec HTML
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
posts_add(
|
|
87
|
+
title: "Mon article",
|
|
88
|
+
html: "<p>Contenu de l'article</p>",
|
|
89
|
+
status: "draft",
|
|
90
|
+
tags: ["tech", "tutorial"]
|
|
91
|
+
)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Uploader une image
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
# Depuis un fichier local
|
|
98
|
+
images_upload(file_path: "/tmp/image.jpg")
|
|
99
|
+
|
|
100
|
+
# Depuis une URL
|
|
101
|
+
images_upload(url: "https://example.com/image.jpg", filename: "image.jpg")
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Supprimer un post
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
posts_delete(id: "xxx", title: "Titre du post", confirm: true)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Modes de réponse
|
|
111
|
+
|
|
112
|
+
### posts_browse
|
|
113
|
+
|
|
114
|
+
| Mode | Tokens/post | Champs |
|
|
115
|
+
|------|-------------|--------|
|
|
116
|
+
| `minimal` | ~15 | id, title, slug, status |
|
|
117
|
+
| `compact` | ~50 | + published_at, updated_at |
|
|
118
|
+
| `standard` | ~150 | + tags, excerpt, reading_time |
|
|
119
|
+
|
|
120
|
+
### posts_read
|
|
121
|
+
|
|
122
|
+
| Mode | Tokens | Champs |
|
|
123
|
+
|------|--------|--------|
|
|
124
|
+
| `metadata` | ~200 | Métadonnées sans contenu |
|
|
125
|
+
| `content` | ~500-2000 | + plaintext (HTML converti) |
|
|
126
|
+
| `full` | ~500-3000 | + HTML brut |
|
|
127
|
+
|
|
128
|
+
## Développement
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npm run dev # Watch mode
|
|
132
|
+
npm run build # Compile TypeScript
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Structure
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
src/
|
|
139
|
+
├── index.ts # Entry point (stdio transport)
|
|
140
|
+
├── server.ts # Définition des tools MCP
|
|
141
|
+
├── api/
|
|
142
|
+
│ ├── auth.ts # Génération JWT
|
|
143
|
+
│ └── client.ts # Client HTTP avec field projection
|
|
144
|
+
└── utils/
|
|
145
|
+
├── fields.ts # Presets de champs par mode
|
|
146
|
+
└── html.ts # Conversion HTML → plaintext
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Licence
|
|
150
|
+
|
|
151
|
+
MIT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ghost Admin API JWT Authentication
|
|
3
|
+
*
|
|
4
|
+
* Ghost uses short-lived JWT tokens for Admin API authentication.
|
|
5
|
+
* Tokens are signed with HS256 using the secret portion of the API key.
|
|
6
|
+
*
|
|
7
|
+
* API Key format: {key_id}:{secret}
|
|
8
|
+
* - key_id: Identifies which key is being used
|
|
9
|
+
* - secret: Hex-encoded secret for signing
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Generate a JWT token for Ghost Admin API authentication
|
|
13
|
+
*
|
|
14
|
+
* @param apiKey - Ghost Admin API key in format "id:secret"
|
|
15
|
+
* @returns Signed JWT token
|
|
16
|
+
* @throws Error if apiKey format is invalid
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateJWT(apiKey: string): string;
|
|
19
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA+ClD"}
|
package/dist/api/auth.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ghost Admin API JWT Authentication
|
|
3
|
+
*
|
|
4
|
+
* Ghost uses short-lived JWT tokens for Admin API authentication.
|
|
5
|
+
* Tokens are signed with HS256 using the secret portion of the API key.
|
|
6
|
+
*
|
|
7
|
+
* API Key format: {key_id}:{secret}
|
|
8
|
+
* - key_id: Identifies which key is being used
|
|
9
|
+
* - secret: Hex-encoded secret for signing
|
|
10
|
+
*/
|
|
11
|
+
import * as crypto from "crypto";
|
|
12
|
+
/**
|
|
13
|
+
* Generate a JWT token for Ghost Admin API authentication
|
|
14
|
+
*
|
|
15
|
+
* @param apiKey - Ghost Admin API key in format "id:secret"
|
|
16
|
+
* @returns Signed JWT token
|
|
17
|
+
* @throws Error if apiKey format is invalid
|
|
18
|
+
*/
|
|
19
|
+
export function generateJWT(apiKey) {
|
|
20
|
+
const parts = apiKey.split(":");
|
|
21
|
+
if (parts.length !== 2) {
|
|
22
|
+
throw new Error('Invalid API key format. Expected "id:secret" but got: ' +
|
|
23
|
+
(parts.length === 1
|
|
24
|
+
? "single value (missing colon)"
|
|
25
|
+
: `${parts.length} parts`));
|
|
26
|
+
}
|
|
27
|
+
const [keyId, keySecret] = parts;
|
|
28
|
+
if (!keyId || !keySecret) {
|
|
29
|
+
throw new Error("API key id or secret is empty");
|
|
30
|
+
}
|
|
31
|
+
// Header
|
|
32
|
+
const header = {
|
|
33
|
+
alg: "HS256",
|
|
34
|
+
typ: "JWT",
|
|
35
|
+
kid: keyId,
|
|
36
|
+
};
|
|
37
|
+
// Payload - 5 minute expiry is recommended by Ghost
|
|
38
|
+
const now = Math.floor(Date.now() / 1000);
|
|
39
|
+
const payload = {
|
|
40
|
+
iat: now,
|
|
41
|
+
exp: now + 300, // 5 minutes
|
|
42
|
+
aud: "/admin/",
|
|
43
|
+
};
|
|
44
|
+
// Encode header and payload
|
|
45
|
+
const encodedHeader = base64url(JSON.stringify(header));
|
|
46
|
+
const encodedPayload = base64url(JSON.stringify(payload));
|
|
47
|
+
const signatureInput = `${encodedHeader}.${encodedPayload}`;
|
|
48
|
+
// Sign with HMAC-SHA256
|
|
49
|
+
const secret = Buffer.from(keySecret, "hex");
|
|
50
|
+
const signature = crypto
|
|
51
|
+
.createHmac("sha256", secret)
|
|
52
|
+
.update(signatureInput)
|
|
53
|
+
.digest();
|
|
54
|
+
const encodedSignature = base64url(signature);
|
|
55
|
+
return `${signatureInput}.${encodedSignature}`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Base64URL encode (RFC 4648)
|
|
59
|
+
* Removes padding and replaces + with - and / with _
|
|
60
|
+
*/
|
|
61
|
+
function base64url(input) {
|
|
62
|
+
const base64 = Buffer.isBuffer(input)
|
|
63
|
+
? input.toString("base64")
|
|
64
|
+
: Buffer.from(input).toString("base64");
|
|
65
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAEhC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,wDAAwD;YACtD,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;gBACjB,CAAC,CAAC,8BAA8B;gBAChC,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,QAAQ,CAAC,CAC/B,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC;IAEjC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IAED,SAAS;IACT,MAAM,MAAM,GAAG;QACb,GAAG,EAAE,OAAO;QACZ,GAAG,EAAE,KAAK;QACV,GAAG,EAAE,KAAK;KACX,CAAC;IAEF,oDAAoD;IACpD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG;QACd,GAAG,EAAE,GAAG;QACR,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,YAAY;QAC5B,GAAG,EAAE,SAAS;KACf,CAAC;IAEF,4BAA4B;IAC5B,MAAM,aAAa,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IACxD,MAAM,cAAc,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1D,MAAM,cAAc,GAAG,GAAG,aAAa,IAAI,cAAc,EAAE,CAAC;IAE5D,wBAAwB;IACxB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,MAAM;SACrB,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC;SAC5B,MAAM,CAAC,cAAc,CAAC;SACtB,MAAM,EAAE,CAAC;IACZ,MAAM,gBAAgB,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IAE9C,OAAO,GAAG,cAAc,IAAI,gBAAgB,EAAE,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAAC,KAAsB;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACnC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC1B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAE1C,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ghost Admin API Client with Field Projection
|
|
3
|
+
*
|
|
4
|
+
* This custom client bypasses @tryghost/admin-api to use the `fields` parameter
|
|
5
|
+
* that the official library doesn't expose, enabling significant token reduction.
|
|
6
|
+
*/
|
|
7
|
+
export interface GhostClientConfig {
|
|
8
|
+
/** Ghost site URL (e.g., https://your-blog.ghost.io) */
|
|
9
|
+
url: string;
|
|
10
|
+
/** Admin API key in format "id:secret" */
|
|
11
|
+
apiKey: string;
|
|
12
|
+
}
|
|
13
|
+
export interface BrowseOptions {
|
|
14
|
+
/** Fields to return (projection) */
|
|
15
|
+
fields?: string[];
|
|
16
|
+
/** NQL filter string */
|
|
17
|
+
filter?: string;
|
|
18
|
+
/** Number of results per page */
|
|
19
|
+
limit?: number;
|
|
20
|
+
/** Page number */
|
|
21
|
+
page?: number;
|
|
22
|
+
/** Sort order (e.g., "published_at desc") */
|
|
23
|
+
order?: string;
|
|
24
|
+
/** Relations to include */
|
|
25
|
+
include?: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface ReadOptions {
|
|
28
|
+
/** Fields to return (projection) */
|
|
29
|
+
fields?: string[];
|
|
30
|
+
/** Relations to include */
|
|
31
|
+
include?: string[];
|
|
32
|
+
/** Formats to include (html, plaintext, mobiledoc) */
|
|
33
|
+
formats?: string[];
|
|
34
|
+
}
|
|
35
|
+
export declare class GhostClient {
|
|
36
|
+
private config;
|
|
37
|
+
private baseUrl;
|
|
38
|
+
constructor(config: GhostClientConfig);
|
|
39
|
+
/**
|
|
40
|
+
* Get authorization headers with fresh JWT token
|
|
41
|
+
*/
|
|
42
|
+
private getHeaders;
|
|
43
|
+
/**
|
|
44
|
+
* Handle API errors
|
|
45
|
+
*/
|
|
46
|
+
private handleResponse;
|
|
47
|
+
/**
|
|
48
|
+
* Browse resources with field projection
|
|
49
|
+
*
|
|
50
|
+
* @param resource - Resource type (posts, tags, members, etc.)
|
|
51
|
+
* @param options - Query options including field projection
|
|
52
|
+
*/
|
|
53
|
+
browse<T>(resource: string, options?: BrowseOptions): Promise<T>;
|
|
54
|
+
/**
|
|
55
|
+
* Read a single resource by ID or slug
|
|
56
|
+
*
|
|
57
|
+
* @param resource - Resource type
|
|
58
|
+
* @param id - Resource ID or "slug/{slug}" for slug lookup
|
|
59
|
+
* @param options - Query options including field projection
|
|
60
|
+
*/
|
|
61
|
+
read<T>(resource: string, id: string, options?: ReadOptions): Promise<T>;
|
|
62
|
+
/**
|
|
63
|
+
* Create a new resource
|
|
64
|
+
*
|
|
65
|
+
* @param resource - Resource type
|
|
66
|
+
* @param data - Resource data
|
|
67
|
+
*/
|
|
68
|
+
add<T>(resource: string, data: Record<string, unknown>): Promise<T>;
|
|
69
|
+
/**
|
|
70
|
+
* Update an existing resource
|
|
71
|
+
*
|
|
72
|
+
* This method automatically fetches the current `updated_at` timestamp
|
|
73
|
+
* to handle Ghost's optimistic locking, so you don't need to provide it.
|
|
74
|
+
*
|
|
75
|
+
* @param resource - Resource type
|
|
76
|
+
* @param id - Resource ID
|
|
77
|
+
* @param data - Fields to update
|
|
78
|
+
*/
|
|
79
|
+
edit<T>(resource: string, id: string, data: Record<string, unknown>): Promise<T>;
|
|
80
|
+
/**
|
|
81
|
+
* Delete a resource
|
|
82
|
+
*
|
|
83
|
+
* @param resource - Resource type
|
|
84
|
+
* @param id - Resource ID
|
|
85
|
+
*/
|
|
86
|
+
delete(resource: string, id: string): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Upload an image to Ghost from a Buffer
|
|
89
|
+
*
|
|
90
|
+
* @param buffer - Image data as Buffer
|
|
91
|
+
* @param filename - Filename with extension (e.g., "cover.jpg")
|
|
92
|
+
* @returns URL of the uploaded image
|
|
93
|
+
*/
|
|
94
|
+
uploadImageFromBuffer(buffer: Buffer, filename: string): Promise<string>;
|
|
95
|
+
/**
|
|
96
|
+
* Upload an image to Ghost from a local file path
|
|
97
|
+
*
|
|
98
|
+
* @param filePath - Absolute path to the image file
|
|
99
|
+
* @returns URL of the uploaded image
|
|
100
|
+
*/
|
|
101
|
+
uploadImageFromPath(filePath: string): Promise<string>;
|
|
102
|
+
/**
|
|
103
|
+
* Upload an image to Ghost from a URL
|
|
104
|
+
*
|
|
105
|
+
* @param imageUrl - URL of the image to download and upload
|
|
106
|
+
* @param filename - Filename with extension
|
|
107
|
+
* @returns URL of the uploaded image
|
|
108
|
+
*/
|
|
109
|
+
uploadImageFromUrl(imageUrl: string, filename: string): Promise<string>;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,iBAAiB;IAChC,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,wBAAwB;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAUD,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,iBAAiB;IAMrC;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;OAEG;YACW,cAAc;IAuB5B;;;;;OAKG;IACG,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAgC1E;;;;;;OAMG;IACG,IAAI,CAAC,CAAC,EACV,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,CAAC,CAAC;IAuBb;;;;;OAKG;IACG,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAczE;;;;;;;;;OASG;IACG,IAAI,CAAC,CAAC,EACV,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,CAAC,CAAC;IAiCb;;;;;OAKG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAazD;;;;;;OAMG;IACG,qBAAqB,CACzB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC;IA+ElB;;;;;OAKG;IACG,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM5D;;;;;;OAMG;IACG,kBAAkB,CACtB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC;CAQnB"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ghost Admin API Client with Field Projection
|
|
3
|
+
*
|
|
4
|
+
* This custom client bypasses @tryghost/admin-api to use the `fields` parameter
|
|
5
|
+
* that the official library doesn't expose, enabling significant token reduction.
|
|
6
|
+
*/
|
|
7
|
+
import { generateJWT } from "./auth.js";
|
|
8
|
+
import * as fs from "fs/promises";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
export class GhostClient {
|
|
11
|
+
config;
|
|
12
|
+
baseUrl;
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
// Remove trailing slash if present
|
|
16
|
+
this.baseUrl = config.url.replace(/\/$/, "");
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get authorization headers with fresh JWT token
|
|
20
|
+
*/
|
|
21
|
+
getHeaders() {
|
|
22
|
+
return {
|
|
23
|
+
Authorization: `Ghost ${generateJWT(this.config.apiKey)}`,
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
"Accept-Version": "v5.0",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Handle API errors
|
|
30
|
+
*/
|
|
31
|
+
async handleResponse(response) {
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
let errorMessage = `Ghost API error: ${response.status} ${response.statusText}`;
|
|
34
|
+
try {
|
|
35
|
+
const errorBody = (await response.json());
|
|
36
|
+
if (errorBody.errors?.[0]) {
|
|
37
|
+
const err = errorBody.errors[0];
|
|
38
|
+
errorMessage = `Ghost API error: ${err.message}`;
|
|
39
|
+
if (err.context) {
|
|
40
|
+
errorMessage += ` (${err.context})`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Ignore JSON parse errors, use default message
|
|
46
|
+
}
|
|
47
|
+
throw new Error(errorMessage);
|
|
48
|
+
}
|
|
49
|
+
return response.json();
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Browse resources with field projection
|
|
53
|
+
*
|
|
54
|
+
* @param resource - Resource type (posts, tags, members, etc.)
|
|
55
|
+
* @param options - Query options including field projection
|
|
56
|
+
*/
|
|
57
|
+
async browse(resource, options = {}) {
|
|
58
|
+
const params = new URLSearchParams();
|
|
59
|
+
if (options.fields?.length) {
|
|
60
|
+
params.set("fields", options.fields.join(","));
|
|
61
|
+
}
|
|
62
|
+
if (options.filter) {
|
|
63
|
+
params.set("filter", options.filter);
|
|
64
|
+
}
|
|
65
|
+
if (options.limit !== undefined) {
|
|
66
|
+
params.set("limit", options.limit.toString());
|
|
67
|
+
}
|
|
68
|
+
if (options.page !== undefined) {
|
|
69
|
+
params.set("page", options.page.toString());
|
|
70
|
+
}
|
|
71
|
+
if (options.order) {
|
|
72
|
+
params.set("order", options.order);
|
|
73
|
+
}
|
|
74
|
+
if (options.include?.length) {
|
|
75
|
+
params.set("include", options.include.join(","));
|
|
76
|
+
}
|
|
77
|
+
const url = `${this.baseUrl}/ghost/api/admin/${resource}/?${params}`;
|
|
78
|
+
const response = await fetch(url, {
|
|
79
|
+
method: "GET",
|
|
80
|
+
headers: this.getHeaders(),
|
|
81
|
+
});
|
|
82
|
+
return this.handleResponse(response);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Read a single resource by ID or slug
|
|
86
|
+
*
|
|
87
|
+
* @param resource - Resource type
|
|
88
|
+
* @param id - Resource ID or "slug/{slug}" for slug lookup
|
|
89
|
+
* @param options - Query options including field projection
|
|
90
|
+
*/
|
|
91
|
+
async read(resource, id, options = {}) {
|
|
92
|
+
const params = new URLSearchParams();
|
|
93
|
+
if (options.fields?.length) {
|
|
94
|
+
params.set("fields", options.fields.join(","));
|
|
95
|
+
}
|
|
96
|
+
if (options.include?.length) {
|
|
97
|
+
params.set("include", options.include.join(","));
|
|
98
|
+
}
|
|
99
|
+
if (options.formats?.length) {
|
|
100
|
+
params.set("formats", options.formats.join(","));
|
|
101
|
+
}
|
|
102
|
+
const url = `${this.baseUrl}/ghost/api/admin/${resource}/${id}/?${params}`;
|
|
103
|
+
const response = await fetch(url, {
|
|
104
|
+
method: "GET",
|
|
105
|
+
headers: this.getHeaders(),
|
|
106
|
+
});
|
|
107
|
+
return this.handleResponse(response);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Create a new resource
|
|
111
|
+
*
|
|
112
|
+
* @param resource - Resource type
|
|
113
|
+
* @param data - Resource data
|
|
114
|
+
*/
|
|
115
|
+
async add(resource, data) {
|
|
116
|
+
// Add ?source=html when creating posts with HTML content
|
|
117
|
+
const sourceParam = resource === "posts" && data.html ? "?source=html" : "";
|
|
118
|
+
const url = `${this.baseUrl}/ghost/api/admin/${resource}/${sourceParam}`;
|
|
119
|
+
const response = await fetch(url, {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: this.getHeaders(),
|
|
122
|
+
body: JSON.stringify({ [resource]: [data] }),
|
|
123
|
+
});
|
|
124
|
+
return this.handleResponse(response);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Update an existing resource
|
|
128
|
+
*
|
|
129
|
+
* This method automatically fetches the current `updated_at` timestamp
|
|
130
|
+
* to handle Ghost's optimistic locking, so you don't need to provide it.
|
|
131
|
+
*
|
|
132
|
+
* @param resource - Resource type
|
|
133
|
+
* @param id - Resource ID
|
|
134
|
+
* @param data - Fields to update
|
|
135
|
+
*/
|
|
136
|
+
async edit(resource, id, data) {
|
|
137
|
+
const current = await this.read(resource, id, { fields: ["updated_at"] });
|
|
138
|
+
const resourceArray = current[resource];
|
|
139
|
+
if (!resourceArray?.[0]?.updated_at) {
|
|
140
|
+
throw new Error(`Could not fetch updated_at for ${resource}/${id}`);
|
|
141
|
+
}
|
|
142
|
+
const updated_at = resourceArray[0].updated_at;
|
|
143
|
+
// Add ?source=html when editing posts with HTML content
|
|
144
|
+
const sourceParam = resource === "posts" && data.html ? "?source=html" : "";
|
|
145
|
+
const url = `${this.baseUrl}/ghost/api/admin/${resource}/${id}/${sourceParam}`;
|
|
146
|
+
const response = await fetch(url, {
|
|
147
|
+
method: "PUT",
|
|
148
|
+
headers: this.getHeaders(),
|
|
149
|
+
body: JSON.stringify({
|
|
150
|
+
[resource]: [{ ...data, updated_at }],
|
|
151
|
+
}),
|
|
152
|
+
});
|
|
153
|
+
return this.handleResponse(response);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Delete a resource
|
|
157
|
+
*
|
|
158
|
+
* @param resource - Resource type
|
|
159
|
+
* @param id - Resource ID
|
|
160
|
+
*/
|
|
161
|
+
async delete(resource, id) {
|
|
162
|
+
const url = `${this.baseUrl}/ghost/api/admin/${resource}/${id}/`;
|
|
163
|
+
const response = await fetch(url, {
|
|
164
|
+
method: "DELETE",
|
|
165
|
+
headers: this.getHeaders(),
|
|
166
|
+
});
|
|
167
|
+
if (!response.ok) {
|
|
168
|
+
await this.handleResponse(response);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Upload an image to Ghost from a Buffer
|
|
173
|
+
*
|
|
174
|
+
* @param buffer - Image data as Buffer
|
|
175
|
+
* @param filename - Filename with extension (e.g., "cover.jpg")
|
|
176
|
+
* @returns URL of the uploaded image
|
|
177
|
+
*/
|
|
178
|
+
async uploadImageFromBuffer(buffer, filename) {
|
|
179
|
+
// Determine MIME type from extension
|
|
180
|
+
const ext = filename.split(".").pop()?.toLowerCase() || "png";
|
|
181
|
+
const mimeTypes = {
|
|
182
|
+
jpg: "image/jpeg",
|
|
183
|
+
jpeg: "image/jpeg",
|
|
184
|
+
png: "image/png",
|
|
185
|
+
gif: "image/gif",
|
|
186
|
+
webp: "image/webp",
|
|
187
|
+
svg: "image/svg+xml",
|
|
188
|
+
};
|
|
189
|
+
const mimeType = mimeTypes[ext] || "application/octet-stream";
|
|
190
|
+
// Build multipart/form-data manually
|
|
191
|
+
const boundary = `----GhostMCPBoundary${Date.now()}`;
|
|
192
|
+
const parts = [];
|
|
193
|
+
// File part
|
|
194
|
+
parts.push(Buffer.from(`--${boundary}\r\n` +
|
|
195
|
+
`Content-Disposition: form-data; name="file"; filename="${filename}"\r\n` +
|
|
196
|
+
`Content-Type: ${mimeType}\r\n\r\n`));
|
|
197
|
+
parts.push(buffer);
|
|
198
|
+
parts.push(Buffer.from("\r\n"));
|
|
199
|
+
// Purpose part (required by Ghost)
|
|
200
|
+
parts.push(Buffer.from(`--${boundary}\r\n` +
|
|
201
|
+
`Content-Disposition: form-data; name="purpose"\r\n\r\n` +
|
|
202
|
+
`image\r\n`));
|
|
203
|
+
// Closing boundary
|
|
204
|
+
parts.push(Buffer.from(`--${boundary}--\r\n`));
|
|
205
|
+
const body = Buffer.concat(parts);
|
|
206
|
+
const url = `${this.baseUrl}/ghost/api/admin/images/upload/`;
|
|
207
|
+
const response = await fetch(url, {
|
|
208
|
+
method: "POST",
|
|
209
|
+
headers: {
|
|
210
|
+
Authorization: `Ghost ${generateJWT(this.config.apiKey)}`,
|
|
211
|
+
"Content-Type": `multipart/form-data; boundary=${boundary}`,
|
|
212
|
+
"Accept-Version": "v5.0",
|
|
213
|
+
},
|
|
214
|
+
body,
|
|
215
|
+
});
|
|
216
|
+
if (!response.ok) {
|
|
217
|
+
let errorMessage = `Ghost API error: ${response.status}`;
|
|
218
|
+
try {
|
|
219
|
+
const errorBody = (await response.json());
|
|
220
|
+
if (errorBody.errors?.[0]?.message) {
|
|
221
|
+
errorMessage = `Ghost API error: ${errorBody.errors[0].message}`;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// Ignore
|
|
226
|
+
}
|
|
227
|
+
throw new Error(errorMessage);
|
|
228
|
+
}
|
|
229
|
+
const result = (await response.json());
|
|
230
|
+
if (!result.images?.[0]?.url) {
|
|
231
|
+
throw new Error("Upload succeeded but no URL returned");
|
|
232
|
+
}
|
|
233
|
+
return result.images[0].url;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Upload an image to Ghost from a local file path
|
|
237
|
+
*
|
|
238
|
+
* @param filePath - Absolute path to the image file
|
|
239
|
+
* @returns URL of the uploaded image
|
|
240
|
+
*/
|
|
241
|
+
async uploadImageFromPath(filePath) {
|
|
242
|
+
const buffer = await fs.readFile(filePath);
|
|
243
|
+
const filename = path.basename(filePath);
|
|
244
|
+
return this.uploadImageFromBuffer(buffer, filename);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Upload an image to Ghost from a URL
|
|
248
|
+
*
|
|
249
|
+
* @param imageUrl - URL of the image to download and upload
|
|
250
|
+
* @param filename - Filename with extension
|
|
251
|
+
* @returns URL of the uploaded image
|
|
252
|
+
*/
|
|
253
|
+
async uploadImageFromUrl(imageUrl, filename) {
|
|
254
|
+
const response = await fetch(imageUrl);
|
|
255
|
+
if (!response.ok) {
|
|
256
|
+
throw new Error(`Failed to download image: ${response.status}`);
|
|
257
|
+
}
|
|
258
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
259
|
+
return this.uploadImageFromBuffer(buffer, filename);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP Ghost Optimized - Entry Point
|
|
4
|
+
*
|
|
5
|
+
* This MCP server provides Ghost Admin API tools with field projection
|
|
6
|
+
* for significant token reduction compared to standard Ghost MCP.
|
|
7
|
+
*
|
|
8
|
+
* Environment variables required:
|
|
9
|
+
* - GHOST_URL: Ghost site URL (e.g., https://your-blog.ghost.io)
|
|
10
|
+
* - GHOST_ADMIN_API_KEY: Admin API key in format "id:secret"
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG"}
|