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 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"}
@@ -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":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAyC7B,MAAM,OAAO,WAAW;IACd,MAAM,CAAoB;IAC1B,OAAO,CAAS;IAExB,YAAY,MAAyB;QACnC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,mCAAmC;QACnC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED;;OAEG;IACK,UAAU;QAChB,OAAO;YACL,aAAa,EAAE,SAAS,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;YACzD,cAAc,EAAE,kBAAkB;YAClC,gBAAgB,EAAE,MAAM;SACzB,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,cAAc,CAAI,QAAkB;QAChD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,IAAI,YAAY,GAAG,oBAAoB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;YAEhF,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuB,CAAC;gBAChE,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC1B,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;oBAChC,YAAY,GAAG,oBAAoB,GAAG,CAAC,OAAO,EAAE,CAAC;oBACjD,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;wBAChB,YAAY,IAAI,KAAK,GAAG,CAAC,OAAO,GAAG,CAAC;oBACtC,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,gDAAgD;YAClD,CAAC;YAED,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;QAChC,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAgB,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,CAAI,QAAgB,EAAE,UAAyB,EAAE;QAC3D,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QAErC,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACjD,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;QACD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,oBAAoB,QAAQ,KAAK,MAAM,EAAE,CAAC;QAErE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE;SAC3B,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC,cAAc,CAAI,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,IAAI,CACR,QAAgB,EAChB,EAAU,EACV,UAAuB,EAAE;QAEzB,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QAErC,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACjD,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,oBAAoB,QAAQ,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;QAE3E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE;SAC3B,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC,cAAc,CAAI,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,GAAG,CAAI,QAAgB,EAAE,IAA6B;QAC1D,yDAAyD;QACzD,MAAM,WAAW,GAAG,QAAQ,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5E,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,oBAAoB,QAAQ,IAAI,WAAW,EAAE,CAAC;QAEzE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE;YAC1B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;SAC7C,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC,cAAc,CAAI,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,IAAI,CACR,QAAgB,EAChB,EAAU,EACV,IAA6B;QAM7B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAC7B,QAAQ,EACR,EAAE,EACF,EAAE,MAAM,EAAE,CAAC,YAAY,CAAC,EAAE,CAC3B,CAAC;QAEF,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,kCAAkC,QAAQ,IAAI,EAAE,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;QAE/C,wDAAwD;QACxD,MAAM,WAAW,GAAG,QAAQ,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5E,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,oBAAoB,QAAQ,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC;QAE/E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE;YAC1B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,CAAC;aACtC,CAAC;SACH,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC,cAAc,CAAI,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,CAAC,QAAgB,EAAE,EAAU;QACvC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,oBAAoB,QAAQ,IAAI,EAAE,GAAG,CAAC;QAEjE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE;SAC3B,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,cAAc,CAAQ,QAAQ,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,qBAAqB,CACzB,MAAc,EACd,QAAgB;QAEhB,qCAAqC;QACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,KAAK,CAAC;QAC9D,MAAM,SAAS,GAA2B;YACxC,GAAG,EAAE,YAAY;YACjB,IAAI,EAAE,YAAY;YAClB,GAAG,EAAE,WAAW;YAChB,GAAG,EAAE,WAAW;YAChB,IAAI,EAAE,YAAY;YAClB,GAAG,EAAE,eAAe;SACrB,CAAC;QACF,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAC;QAE9D,qCAAqC;QACrC,MAAM,QAAQ,GAAG,uBAAuB,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACrD,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,YAAY;QACZ,KAAK,CAAC,IAAI,CACR,MAAM,CAAC,IAAI,CACT,KAAK,QAAQ,MAAM;YACjB,0DAA0D,QAAQ,OAAO;YACzE,iBAAiB,QAAQ,UAAU,CACtC,CACF,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAEhC,mCAAmC;QACnC,KAAK,CAAC,IAAI,CACR,MAAM,CAAC,IAAI,CACT,KAAK,QAAQ,MAAM;YACjB,wDAAwD;YACxD,WAAW,CACd,CACF,CAAC;QAEF,mBAAmB;QACnB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,QAAQ,QAAQ,CAAC,CAAC,CAAC;QAE/C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAElC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,iCAAiC,CAAC;QAE7D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,SAAS,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;gBACzD,cAAc,EAAE,iCAAiC,QAAQ,EAAE;gBAC3D,gBAAgB,EAAE,MAAM;aACzB;YACD,IAAI;SACL,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,IAAI,YAAY,GAAG,oBAAoB,QAAQ,CAAC,MAAM,EAAE,CAAC;YACzD,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuB,CAAC;gBAChE,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;oBACnC,YAAY,GAAG,oBAAoB,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;gBACnE,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;QAChC,CAAC;QAKD,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAwB,CAAC;QAE9D,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC1D,CAAC;QAED,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC9B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,mBAAmB,CAAC,QAAgB;QACxC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACtD,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,kBAAkB,CACtB,QAAgB,EAChB,QAAgB;QAEhB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;QACvC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACtD,CAAC;CACF"}
@@ -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"}