@ampless/mcp-server 0.2.0-alpha.9 → 1.0.0-alpha.10
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.
|
@@ -8,7 +8,6 @@ var POST_FIELDS = (
|
|
|
8
8
|
/* GraphQL */
|
|
9
9
|
`
|
|
10
10
|
fragment PostFields on Post {
|
|
11
|
-
siteId
|
|
12
11
|
postId
|
|
13
12
|
slug
|
|
14
13
|
title
|
|
@@ -25,7 +24,6 @@ var POST_FIELDS = (
|
|
|
25
24
|
function toCorePost(p) {
|
|
26
25
|
const metadata = decodeAwsJson(p.metadata);
|
|
27
26
|
return {
|
|
28
|
-
siteId: p.siteId,
|
|
29
27
|
postId: p.postId,
|
|
30
28
|
slug: p.slug,
|
|
31
29
|
title: p.title,
|
|
@@ -57,7 +55,6 @@ var QUERY = (
|
|
|
57
55
|
var listPostsSchema = {
|
|
58
56
|
type: "object",
|
|
59
57
|
properties: {
|
|
60
|
-
siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
|
|
61
58
|
status: {
|
|
62
59
|
type: "string",
|
|
63
60
|
enum: ["draft", "published", "all"],
|
|
@@ -67,12 +64,16 @@ var listPostsSchema = {
|
|
|
67
64
|
nextToken: { type: "string", description: "Pagination cursor from a previous call" }
|
|
68
65
|
}
|
|
69
66
|
};
|
|
70
|
-
async function listPosts(client,
|
|
71
|
-
const siteId = args.siteId ?? defaultSiteId;
|
|
67
|
+
async function listPosts(client, args = {}) {
|
|
72
68
|
const status = args.status ?? "all";
|
|
73
|
-
const filter = {
|
|
69
|
+
const filter = {};
|
|
74
70
|
if (status !== "all") filter.status = { eq: status };
|
|
75
|
-
const
|
|
71
|
+
const hasFilter = Object.keys(filter).length > 0;
|
|
72
|
+
const data = await client.query(QUERY, {
|
|
73
|
+
filter: hasFilter ? filter : void 0,
|
|
74
|
+
limit: args.limit ?? 20,
|
|
75
|
+
nextToken: args.nextToken
|
|
76
|
+
});
|
|
76
77
|
return {
|
|
77
78
|
posts: data.listPosts.items.map(toCorePost),
|
|
78
79
|
nextToken: data.listPosts.nextToken
|
|
@@ -84,8 +85,8 @@ var GET_BY_ID = (
|
|
|
84
85
|
/* GraphQL */
|
|
85
86
|
`
|
|
86
87
|
${POST_FIELDS}
|
|
87
|
-
query GetPost($
|
|
88
|
-
getPost(
|
|
88
|
+
query GetPost($postId: ID!) {
|
|
89
|
+
getPost(postId: $postId) {
|
|
89
90
|
...PostFields
|
|
90
91
|
}
|
|
91
92
|
}
|
|
@@ -107,22 +108,20 @@ var LIST_BY_SLUG = (
|
|
|
107
108
|
var getPostSchema = {
|
|
108
109
|
type: "object",
|
|
109
110
|
properties: {
|
|
110
|
-
siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
|
|
111
111
|
slug: { type: "string", description: "Post slug" },
|
|
112
112
|
postId: { type: "string", description: "Post id (used when slug is omitted)" }
|
|
113
113
|
}
|
|
114
114
|
};
|
|
115
|
-
async function getPost(client,
|
|
115
|
+
async function getPost(client, args) {
|
|
116
116
|
if (!args.slug && !args.postId) {
|
|
117
117
|
throw new Error("get_post requires either `slug` or `postId`");
|
|
118
118
|
}
|
|
119
|
-
const siteId = args.siteId ?? defaultSiteId;
|
|
120
119
|
if (args.postId) {
|
|
121
|
-
const data2 = await client.query(GET_BY_ID, {
|
|
120
|
+
const data2 = await client.query(GET_BY_ID, { postId: args.postId });
|
|
122
121
|
return data2.getPost ? toCorePost(data2.getPost) : null;
|
|
123
122
|
}
|
|
124
123
|
const data = await client.query(LIST_BY_SLUG, {
|
|
125
|
-
filter: {
|
|
124
|
+
filter: { slug: { eq: args.slug } }
|
|
126
125
|
});
|
|
127
126
|
const item = data.listPosts.items[0];
|
|
128
127
|
return item ? toCorePost(item) : null;
|
|
@@ -130,8 +129,6 @@ async function getPost(client, defaultSiteId, args) {
|
|
|
130
129
|
|
|
131
130
|
// src/tools/create-post.ts
|
|
132
131
|
import {
|
|
133
|
-
composeSiteIdStatus,
|
|
134
|
-
composeSiteIdSlug,
|
|
135
132
|
encodeAwsJson
|
|
136
133
|
} from "ampless";
|
|
137
134
|
|
|
@@ -139,19 +136,19 @@ import {
|
|
|
139
136
|
function entries(post) {
|
|
140
137
|
if (post.status !== "published" || !post.publishedAt || !post.tags?.length) return [];
|
|
141
138
|
return post.tags.map((tag) => ({
|
|
142
|
-
|
|
139
|
+
tag,
|
|
143
140
|
publishedAtPostId: `${post.publishedAt}#${post.postId}`
|
|
144
141
|
}));
|
|
145
142
|
}
|
|
146
143
|
function entryKey(e) {
|
|
147
|
-
return `${e.
|
|
144
|
+
return `${e.tag}|${e.publishedAtPostId}`;
|
|
148
145
|
}
|
|
149
146
|
var CREATE_POST_TAG = (
|
|
150
147
|
/* GraphQL */
|
|
151
148
|
`
|
|
152
149
|
mutation CreatePostTag($input: CreatePostTagInput!) {
|
|
153
150
|
createPostTag(input: $input) {
|
|
154
|
-
|
|
151
|
+
tag
|
|
155
152
|
publishedAtPostId
|
|
156
153
|
}
|
|
157
154
|
}
|
|
@@ -162,7 +159,7 @@ var UPDATE_POST_TAG = (
|
|
|
162
159
|
`
|
|
163
160
|
mutation UpdatePostTag($input: UpdatePostTagInput!) {
|
|
164
161
|
updatePostTag(input: $input) {
|
|
165
|
-
|
|
162
|
+
tag
|
|
166
163
|
publishedAtPostId
|
|
167
164
|
}
|
|
168
165
|
}
|
|
@@ -173,7 +170,7 @@ var DELETE_POST_TAG = (
|
|
|
173
170
|
`
|
|
174
171
|
mutation DeletePostTag($input: DeletePostTagInput!) {
|
|
175
172
|
deletePostTag(input: $input) {
|
|
176
|
-
|
|
173
|
+
tag
|
|
177
174
|
publishedAtPostId
|
|
178
175
|
}
|
|
179
176
|
}
|
|
@@ -187,7 +184,7 @@ async function syncPostTags(client, post, oldPost) {
|
|
|
187
184
|
await Promise.all(
|
|
188
185
|
oldEntries.filter((e) => !newKeys.has(entryKey(e))).map(
|
|
189
186
|
(e) => client.query(DELETE_POST_TAG, {
|
|
190
|
-
input: {
|
|
187
|
+
input: { tag: e.tag, publishedAtPostId: e.publishedAtPostId }
|
|
191
188
|
})
|
|
192
189
|
)
|
|
193
190
|
);
|
|
@@ -195,10 +192,8 @@ async function syncPostTags(client, post, oldPost) {
|
|
|
195
192
|
newEntries.filter((e) => !oldKeys.has(entryKey(e))).map(
|
|
196
193
|
(e) => client.query(CREATE_POST_TAG, {
|
|
197
194
|
input: {
|
|
198
|
-
|
|
195
|
+
tag: e.tag,
|
|
199
196
|
publishedAtPostId: e.publishedAtPostId,
|
|
200
|
-
siteId: post.siteId,
|
|
201
|
-
tag: e.siteIdTag.slice(post.siteId.length + 1),
|
|
202
197
|
postId: post.postId,
|
|
203
198
|
publishedAt: post.publishedAt,
|
|
204
199
|
slug: post.slug,
|
|
@@ -213,7 +208,7 @@ async function syncPostTags(client, post, oldPost) {
|
|
|
213
208
|
newEntries.filter((e) => oldKeys.has(entryKey(e))).map(
|
|
214
209
|
(e) => client.query(UPDATE_POST_TAG, {
|
|
215
210
|
input: {
|
|
216
|
-
|
|
211
|
+
tag: e.tag,
|
|
217
212
|
publishedAtPostId: e.publishedAtPostId,
|
|
218
213
|
slug: post.slug,
|
|
219
214
|
title: post.title,
|
|
@@ -241,7 +236,6 @@ var createPostSchema = {
|
|
|
241
236
|
type: "object",
|
|
242
237
|
required: ["slug", "title", "format", "body"],
|
|
243
238
|
properties: {
|
|
244
|
-
siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
|
|
245
239
|
postId: { type: "string", description: "Optional explicit id; auto-generated if omitted" },
|
|
246
240
|
slug: { type: "string" },
|
|
247
241
|
title: { type: "string" },
|
|
@@ -259,7 +253,7 @@ var createPostSchema = {
|
|
|
259
253
|
tags: { type: "array", items: { type: "string" } },
|
|
260
254
|
metadata: {
|
|
261
255
|
type: "object",
|
|
262
|
-
description: "Free-form per-post metadata. Reserved well-known keys: `no_layout` (boolean) \u2014 when true, the public page is served as bare HTML with no theme chrome (the route redirects to /
|
|
256
|
+
description: "Free-form per-post metadata. Reserved well-known keys: `no_layout` (boolean) \u2014 when true, the public page is served as bare HTML with no theme chrome (the route redirects to /_/<slug>); meaningful only with format=html. Other keys pass through unchanged for themes/plugins.",
|
|
263
257
|
properties: {
|
|
264
258
|
no_layout: {
|
|
265
259
|
type: "boolean",
|
|
@@ -270,14 +264,12 @@ var createPostSchema = {
|
|
|
270
264
|
}
|
|
271
265
|
}
|
|
272
266
|
};
|
|
273
|
-
async function createPost(client,
|
|
274
|
-
const siteId = args.siteId ?? defaultSiteId;
|
|
267
|
+
async function createPost(client, args) {
|
|
275
268
|
const postId = args.postId ?? `post-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
276
269
|
const status = args.status ?? "draft";
|
|
277
270
|
const publishedAt = args.publishedAt ?? (status === "published" ? (/* @__PURE__ */ new Date()).toISOString() : void 0);
|
|
278
271
|
const data = await client.query(MUTATION, {
|
|
279
272
|
input: {
|
|
280
|
-
siteId,
|
|
281
273
|
postId,
|
|
282
274
|
slug: args.slug,
|
|
283
275
|
title: args.title,
|
|
@@ -287,10 +279,7 @@ async function createPost(client, defaultSiteId, args) {
|
|
|
287
279
|
status,
|
|
288
280
|
publishedAt,
|
|
289
281
|
tags: args.tags,
|
|
290
|
-
metadata: args.metadata !== void 0 ? encodeAwsJson(args.metadata) : void 0
|
|
291
|
-
// Denormalized GSI keys.
|
|
292
|
-
siteIdStatus: composeSiteIdStatus(siteId, status),
|
|
293
|
-
siteIdSlug: composeSiteIdSlug(siteId, args.slug)
|
|
282
|
+
metadata: args.metadata !== void 0 ? encodeAwsJson(args.metadata) : void 0
|
|
294
283
|
}
|
|
295
284
|
});
|
|
296
285
|
const created = toCorePost(data.createPost);
|
|
@@ -300,8 +289,6 @@ async function createPost(client, defaultSiteId, args) {
|
|
|
300
289
|
|
|
301
290
|
// src/tools/update-post.ts
|
|
302
291
|
import {
|
|
303
|
-
composeSiteIdStatus as composeSiteIdStatus2,
|
|
304
|
-
composeSiteIdSlug as composeSiteIdSlug2,
|
|
305
292
|
encodeAwsJson as encodeAwsJson2
|
|
306
293
|
} from "ampless";
|
|
307
294
|
var MUTATION2 = (
|
|
@@ -320,7 +307,6 @@ var updatePostSchema = {
|
|
|
320
307
|
required: ["postId"],
|
|
321
308
|
properties: {
|
|
322
309
|
postId: { type: "string" },
|
|
323
|
-
siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
|
|
324
310
|
slug: { type: "string" },
|
|
325
311
|
title: { type: "string" },
|
|
326
312
|
excerpt: { type: "string" },
|
|
@@ -335,7 +321,7 @@ var updatePostSchema = {
|
|
|
335
321
|
tags: { type: "array", items: { type: "string" } },
|
|
336
322
|
metadata: {
|
|
337
323
|
type: "object",
|
|
338
|
-
description: "Free-form per-post metadata. Reserved well-known keys: `no_layout` (boolean) \u2014 when true, the public page is served as bare HTML with no theme chrome (the route redirects to /
|
|
324
|
+
description: "Free-form per-post metadata. Reserved well-known keys: `no_layout` (boolean) \u2014 when true, the public page is served as bare HTML with no theme chrome (the route redirects to /_/<slug>); meaningful only with format=html. Passing metadata replaces the existing metadata object \u2014 read the current post first if you only want to add a key.",
|
|
339
325
|
properties: {
|
|
340
326
|
no_layout: {
|
|
341
327
|
type: "boolean",
|
|
@@ -346,10 +332,9 @@ var updatePostSchema = {
|
|
|
346
332
|
}
|
|
347
333
|
}
|
|
348
334
|
};
|
|
349
|
-
async function updatePost(client,
|
|
350
|
-
const
|
|
351
|
-
const
|
|
352
|
-
const input = { siteId, postId: args.postId };
|
|
335
|
+
async function updatePost(client, args) {
|
|
336
|
+
const oldPost = await getPost(client, { postId: args.postId });
|
|
337
|
+
const input = { postId: args.postId };
|
|
353
338
|
if (args.slug !== void 0) input.slug = args.slug;
|
|
354
339
|
if (args.title !== void 0) input.title = args.title;
|
|
355
340
|
if (args.excerpt !== void 0) input.excerpt = args.excerpt;
|
|
@@ -359,12 +344,6 @@ async function updatePost(client, defaultSiteId, args) {
|
|
|
359
344
|
if (args.publishedAt !== void 0) input.publishedAt = args.publishedAt;
|
|
360
345
|
if (args.tags !== void 0) input.tags = args.tags;
|
|
361
346
|
if (args.metadata !== void 0) input.metadata = encodeAwsJson2(args.metadata);
|
|
362
|
-
if (args.status !== void 0) {
|
|
363
|
-
input.siteIdStatus = composeSiteIdStatus2(siteId, args.status);
|
|
364
|
-
}
|
|
365
|
-
if (args.slug !== void 0) {
|
|
366
|
-
input.siteIdSlug = composeSiteIdSlug2(siteId, args.slug);
|
|
367
|
-
}
|
|
368
347
|
const data = await client.query(MUTATION2, { input });
|
|
369
348
|
const updated = toCorePost(data.updatePost);
|
|
370
349
|
await syncPostTags(client, updated, oldPost);
|
|
@@ -377,7 +356,6 @@ var MUTATION3 = (
|
|
|
377
356
|
`
|
|
378
357
|
mutation DeletePost($input: DeletePostInput!) {
|
|
379
358
|
deletePost(input: $input) {
|
|
380
|
-
siteId
|
|
381
359
|
postId
|
|
382
360
|
}
|
|
383
361
|
}
|
|
@@ -387,17 +365,15 @@ var deletePostSchema = {
|
|
|
387
365
|
type: "object",
|
|
388
366
|
required: ["postId"],
|
|
389
367
|
properties: {
|
|
390
|
-
postId: { type: "string" }
|
|
391
|
-
siteId: { type: "string", description: 'Site identifier (defaults to "default")' }
|
|
368
|
+
postId: { type: "string" }
|
|
392
369
|
}
|
|
393
370
|
};
|
|
394
|
-
async function deletePost(client,
|
|
395
|
-
const
|
|
396
|
-
const oldPost = await getPost(client, defaultSiteId, { postId: args.postId, siteId });
|
|
371
|
+
async function deletePost(client, args) {
|
|
372
|
+
const oldPost = await getPost(client, { postId: args.postId });
|
|
397
373
|
if (oldPost) {
|
|
398
374
|
await syncPostTags(client, { ...oldPost, status: "draft" }, oldPost);
|
|
399
375
|
}
|
|
400
|
-
const data = await client.query(MUTATION3, { input: {
|
|
376
|
+
const data = await client.query(MUTATION3, { input: { postId: args.postId } });
|
|
401
377
|
return { deleted: data.deletePost };
|
|
402
378
|
}
|
|
403
379
|
|
|
@@ -418,7 +394,6 @@ var MUTATION4 = (
|
|
|
418
394
|
`
|
|
419
395
|
mutation CreateMedia($input: CreateMediaInput!) {
|
|
420
396
|
createMedia(input: $input) {
|
|
421
|
-
siteId
|
|
422
397
|
mediaId
|
|
423
398
|
src
|
|
424
399
|
mimeType
|
|
@@ -432,7 +407,6 @@ var uploadMediaSchema = {
|
|
|
432
407
|
type: "object",
|
|
433
408
|
required: ["filename", "mimeType", "base64Data"],
|
|
434
409
|
properties: {
|
|
435
|
-
siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
|
|
436
410
|
filename: { type: "string", description: "Original filename; sanitized server-side" },
|
|
437
411
|
mimeType: {
|
|
438
412
|
type: "string",
|
|
@@ -444,15 +418,13 @@ var uploadMediaSchema = {
|
|
|
444
418
|
}
|
|
445
419
|
}
|
|
446
420
|
};
|
|
447
|
-
async function uploadMedia(graphql, storage,
|
|
448
|
-
const siteId = args.siteId ?? defaultSiteId;
|
|
421
|
+
async function uploadMedia(graphql, storage, args) {
|
|
449
422
|
const body = Buffer.from(args.base64Data, "base64");
|
|
450
423
|
const key = buildMediaKey(args.filename);
|
|
451
424
|
const url = await storage.putObject(key, body, args.mimeType);
|
|
452
425
|
const mediaId = `media-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
453
426
|
const data = await graphql.query(MUTATION4, {
|
|
454
427
|
input: {
|
|
455
|
-
siteId,
|
|
456
428
|
mediaId,
|
|
457
429
|
src: key,
|
|
458
430
|
mimeType: args.mimeType,
|
|
@@ -473,11 +445,10 @@ function getSchema() {
|
|
|
473
445
|
contentTypes: [
|
|
474
446
|
{
|
|
475
447
|
name: "post",
|
|
476
|
-
identifier: ["
|
|
448
|
+
identifier: ["postId"],
|
|
477
449
|
fields: {
|
|
478
|
-
siteId: { type: "string", required: true },
|
|
479
450
|
postId: { type: "string", required: true, description: "auto-generated if omitted on create" },
|
|
480
|
-
slug: { type: "string", required: true, description: "URL slug, unique
|
|
451
|
+
slug: { type: "string", required: true, description: "URL slug, unique" },
|
|
481
452
|
title: { type: "string", required: true },
|
|
482
453
|
excerpt: { type: "string" },
|
|
483
454
|
format: { type: "enum", values: ["tiptap", "markdown", "html"], required: true },
|
|
@@ -496,9 +467,8 @@ function getSchema() {
|
|
|
496
467
|
},
|
|
497
468
|
{
|
|
498
469
|
name: "page",
|
|
499
|
-
identifier: ["
|
|
470
|
+
identifier: ["pageId"],
|
|
500
471
|
fields: {
|
|
501
|
-
siteId: { type: "string", required: true },
|
|
502
472
|
pageId: { type: "string", required: true },
|
|
503
473
|
slug: { type: "string", required: true },
|
|
504
474
|
title: { type: "string", required: true },
|
|
@@ -510,9 +480,8 @@ function getSchema() {
|
|
|
510
480
|
},
|
|
511
481
|
{
|
|
512
482
|
name: "media",
|
|
513
|
-
identifier: ["
|
|
483
|
+
identifier: ["mediaId"],
|
|
514
484
|
fields: {
|
|
515
|
-
siteId: { type: "string", required: true },
|
|
516
485
|
mediaId: { type: "string", required: true },
|
|
517
486
|
src: { type: "string", required: true, description: "S3 key" },
|
|
518
487
|
mimeType: { type: "string", required: true },
|
|
@@ -525,8 +494,8 @@ function getSchema() {
|
|
|
525
494
|
notes: {
|
|
526
495
|
editorTrust: 'editor stores arbitrary HTML/JS verbatim \u2014 same trust shape as WordPress unfiltered_html capability. See docs/architecture/04-access-layer-mcp.md \xA7"editor \u306E\u4FE1\u983C\u30E2\u30C7\u30EB".',
|
|
527
496
|
tiptapBody: 'When format=tiptap, body is the tiptap document JSON: { type: "doc", content: [...] }. The renderer expects this shape.',
|
|
528
|
-
noLayout: "metadata.no_layout=true serves the post as bare HTML with no theme chrome \u2014 the public route at /<slug>
|
|
529
|
-
staticFormat: "A fourth format value `static` exists on the underlying data model for posts whose body is a JSON manifest pointing to a pre-uploaded HTML/CSS/JS bundle in S3 at public/static/<
|
|
497
|
+
noLayout: "metadata.no_layout=true serves the post as bare HTML with no theme chrome \u2014 the public route at /<slug> 308-redirects to /_/<slug>, and that route renders the body verbatim with no wrapping <html>/<head>/layout. Use this for landing pages, embeds, or any post whose body is a full HTML document. Only meaningful with format=html (the other formats need the theme renderer).",
|
|
498
|
+
staticFormat: "A fourth format value `static` exists on the underlying data model for posts whose body is a JSON manifest pointing to a pre-uploaded HTML/CSS/JS bundle in S3 at public/static/<slug>/. Public URL pattern: /_/<slug>/ for the entrypoint and /_/<slug>/<file> for every bundle file (308 redirect from /<slug> via the post dispatcher). Static posts are created/edited through the dedicated tools `upload_static_bundle` (zip in one shot), `upload_static_file` / `delete_static_file` (incremental per-file ops), and `commit_static_post` (rebuild the manifest from the current S3 prefix). `create_post` / `update_post` intentionally do NOT accept format=static \u2014 the bundle tools are the only supported entry point so the Post manifest stays in sync with the S3 prefix."
|
|
530
499
|
}
|
|
531
500
|
};
|
|
532
501
|
}
|
|
@@ -576,8 +545,6 @@ function decodeUtf8(data) {
|
|
|
576
545
|
|
|
577
546
|
// src/tools/upsert-static-post.ts
|
|
578
547
|
import {
|
|
579
|
-
composeSiteIdStatus as composeSiteIdStatus3,
|
|
580
|
-
composeSiteIdSlug as composeSiteIdSlug3,
|
|
581
548
|
encodeAwsJson as encodeAwsJson3
|
|
582
549
|
} from "ampless";
|
|
583
550
|
var CREATE_MUTATION = (
|
|
@@ -602,21 +569,17 @@ var UPDATE_MUTATION = (
|
|
|
602
569
|
}
|
|
603
570
|
`
|
|
604
571
|
);
|
|
605
|
-
async function upsertStaticPost(graphql,
|
|
606
|
-
const existing = await getPost(graphql,
|
|
572
|
+
async function upsertStaticPost(graphql, slug, body, fields) {
|
|
573
|
+
const existing = await getPost(graphql, { slug });
|
|
607
574
|
if (existing) {
|
|
608
575
|
const input2 = {
|
|
609
|
-
siteId,
|
|
610
576
|
postId: existing.postId,
|
|
611
577
|
format: "static",
|
|
612
578
|
body: encodeAwsJson3(body)
|
|
613
579
|
};
|
|
614
580
|
if (fields.title !== void 0) input2.title = fields.title;
|
|
615
581
|
if (fields.excerpt !== void 0) input2.excerpt = fields.excerpt;
|
|
616
|
-
if (fields.status !== void 0)
|
|
617
|
-
input2.status = fields.status;
|
|
618
|
-
input2.siteIdStatus = composeSiteIdStatus3(siteId, fields.status);
|
|
619
|
-
}
|
|
582
|
+
if (fields.status !== void 0) input2.status = fields.status;
|
|
620
583
|
if (fields.publishedAt !== void 0) input2.publishedAt = fields.publishedAt;
|
|
621
584
|
if (fields.tags !== void 0) input2.tags = fields.tags;
|
|
622
585
|
if (fields.metadata !== void 0) {
|
|
@@ -636,15 +599,12 @@ async function upsertStaticPost(graphql, siteId, slug, body, fields) {
|
|
|
636
599
|
const publishedAt = fields.publishedAt ?? (status === "published" ? (/* @__PURE__ */ new Date()).toISOString() : void 0);
|
|
637
600
|
const postId = fields.postId ?? `post-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
638
601
|
const input = {
|
|
639
|
-
siteId,
|
|
640
602
|
postId,
|
|
641
603
|
slug,
|
|
642
604
|
title: fields.title,
|
|
643
605
|
format: "static",
|
|
644
606
|
body: encodeAwsJson3(body),
|
|
645
|
-
status
|
|
646
|
-
siteIdStatus: composeSiteIdStatus3(siteId, status),
|
|
647
|
-
siteIdSlug: composeSiteIdSlug3(siteId, slug)
|
|
607
|
+
status
|
|
648
608
|
};
|
|
649
609
|
if (fields.excerpt !== void 0) input.excerpt = fields.excerpt;
|
|
650
610
|
if (publishedAt !== void 0) input.publishedAt = publishedAt;
|
|
@@ -661,7 +621,6 @@ var uploadStaticBundleSchema = {
|
|
|
661
621
|
type: "object",
|
|
662
622
|
required: ["slug", "title", "zipBase64"],
|
|
663
623
|
properties: {
|
|
664
|
-
siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
|
|
665
624
|
postId: {
|
|
666
625
|
type: "string",
|
|
667
626
|
description: "Optional explicit Post id when creating a new post. Ignored if a post at this slug already exists."
|
|
@@ -687,8 +646,7 @@ var uploadStaticBundleSchema = {
|
|
|
687
646
|
}
|
|
688
647
|
}
|
|
689
648
|
};
|
|
690
|
-
async function uploadStaticBundle(graphql, storage,
|
|
691
|
-
const siteId = args.siteId ?? defaultSiteId;
|
|
649
|
+
async function uploadStaticBundle(graphql, storage, args) {
|
|
692
650
|
const slug = args.slug;
|
|
693
651
|
const zipBytes = Buffer.from(args.zipBase64, "base64");
|
|
694
652
|
if (zipBytes.length === 0) {
|
|
@@ -715,7 +673,7 @@ async function uploadStaticBundle(graphql, storage, defaultSiteId, args) {
|
|
|
715
673
|
`upload_static_bundle: entrypoint "${entrypoint}" is not present in the bundle.`
|
|
716
674
|
);
|
|
717
675
|
}
|
|
718
|
-
const prefix = bundlePrefix(
|
|
676
|
+
const prefix = bundlePrefix(slug);
|
|
719
677
|
const existing = await storage.listObjects(prefix).catch((err) => {
|
|
720
678
|
console.error("[upload_static_bundle] listObjects failed (proceeding)", err);
|
|
721
679
|
return [];
|
|
@@ -733,7 +691,7 @@ async function uploadStaticBundle(graphql, storage, defaultSiteId, args) {
|
|
|
733
691
|
files: files.map((f) => f.path).sort(),
|
|
734
692
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
735
693
|
};
|
|
736
|
-
const { post } = await upsertStaticPost(graphql,
|
|
694
|
+
const { post } = await upsertStaticPost(graphql, slug, body, {
|
|
737
695
|
title: args.title,
|
|
738
696
|
postId: args.postId,
|
|
739
697
|
status: args.status,
|
|
@@ -757,8 +715,7 @@ var uploadStaticFileSchema = {
|
|
|
757
715
|
type: "object",
|
|
758
716
|
required: ["slug", "filename", "base64Data"],
|
|
759
717
|
properties: {
|
|
760
|
-
|
|
761
|
-
slug: { type: "string", description: "Bundle slug. Files land at public/static/<siteId>/<slug>/<filename>." },
|
|
718
|
+
slug: { type: "string", description: "Bundle slug. Files land at public/static/<slug>/<filename>." },
|
|
762
719
|
filename: {
|
|
763
720
|
type: "string",
|
|
764
721
|
description: "Relative path inside the bundle. Must NOT start with `/`, contain `..`, or include null bytes. macOS / Windows zip junk (`.DS_Store`, `__MACOSX/*`, `Thumbs.db`) is also rejected."
|
|
@@ -773,8 +730,7 @@ var uploadStaticFileSchema = {
|
|
|
773
730
|
}
|
|
774
731
|
}
|
|
775
732
|
};
|
|
776
|
-
async function uploadStaticFile(storage,
|
|
777
|
-
const siteId = args.siteId ?? defaultSiteId;
|
|
733
|
+
async function uploadStaticFile(storage, args) {
|
|
778
734
|
const filename = args.filename;
|
|
779
735
|
const reason = validateBundlePath2(filename);
|
|
780
736
|
if (reason) {
|
|
@@ -795,7 +751,7 @@ async function uploadStaticFile(storage, defaultSiteId, args) {
|
|
|
795
751
|
}
|
|
796
752
|
}
|
|
797
753
|
const contentType = args.contentType ?? mimeTypeFor2(filename);
|
|
798
|
-
const key = `${bundlePrefix2(
|
|
754
|
+
const key = `${bundlePrefix2(args.slug)}${filename}`;
|
|
799
755
|
const url = await storage.putObject(key, body, contentType);
|
|
800
756
|
return { key, url, size: body.length };
|
|
801
757
|
}
|
|
@@ -806,7 +762,6 @@ var deleteStaticFileSchema = {
|
|
|
806
762
|
type: "object",
|
|
807
763
|
required: ["slug", "filename"],
|
|
808
764
|
properties: {
|
|
809
|
-
siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
|
|
810
765
|
slug: { type: "string" },
|
|
811
766
|
filename: {
|
|
812
767
|
type: "string",
|
|
@@ -814,13 +769,12 @@ var deleteStaticFileSchema = {
|
|
|
814
769
|
}
|
|
815
770
|
}
|
|
816
771
|
};
|
|
817
|
-
async function deleteStaticFile(storage,
|
|
818
|
-
const siteId = args.siteId ?? defaultSiteId;
|
|
772
|
+
async function deleteStaticFile(storage, args) {
|
|
819
773
|
const reason = validateBundlePath3(args.filename);
|
|
820
774
|
if (reason) {
|
|
821
775
|
throw new Error(`delete_static_file: invalid filename "${args.filename}" (${reason})`);
|
|
822
776
|
}
|
|
823
|
-
const prefix = bundlePrefix3(
|
|
777
|
+
const prefix = bundlePrefix3(args.slug);
|
|
824
778
|
const key = `${prefix}${args.filename}`;
|
|
825
779
|
const existing = await storage.listObjects(key);
|
|
826
780
|
const found = existing.some((o) => o.key === key);
|
|
@@ -840,7 +794,6 @@ var commitStaticPostSchema = {
|
|
|
840
794
|
type: "object",
|
|
841
795
|
required: ["slug"],
|
|
842
796
|
properties: {
|
|
843
|
-
siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
|
|
844
797
|
slug: { type: "string" },
|
|
845
798
|
title: {
|
|
846
799
|
type: "string",
|
|
@@ -865,10 +818,9 @@ var commitStaticPostSchema = {
|
|
|
865
818
|
}
|
|
866
819
|
}
|
|
867
820
|
};
|
|
868
|
-
async function commitStaticPost(graphql, storage,
|
|
869
|
-
const siteId = args.siteId ?? defaultSiteId;
|
|
821
|
+
async function commitStaticPost(graphql, storage, args) {
|
|
870
822
|
const slug = args.slug;
|
|
871
|
-
const prefix = bundlePrefix4(
|
|
823
|
+
const prefix = bundlePrefix4(slug);
|
|
872
824
|
const objects = await storage.listObjects(prefix);
|
|
873
825
|
if (objects.length === 0) {
|
|
874
826
|
throw new Error(
|
|
@@ -887,7 +839,7 @@ async function commitStaticPost(graphql, storage, defaultSiteId, args) {
|
|
|
887
839
|
files: relPaths,
|
|
888
840
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
889
841
|
};
|
|
890
|
-
const { post, created } = await upsertStaticPost(graphql,
|
|
842
|
+
const { post, created } = await upsertStaticPost(graphql, slug, body, {
|
|
891
843
|
title: args.title,
|
|
892
844
|
postId: args.postId,
|
|
893
845
|
excerpt: args.excerpt,
|
|
@@ -905,37 +857,37 @@ var tools = [
|
|
|
905
857
|
name: "list_posts",
|
|
906
858
|
description: "List posts in the CMS with optional filters by status. Returns up to `limit` posts (default 20) plus a `nextToken` cursor for pagination.",
|
|
907
859
|
inputSchema: listPostsSchema,
|
|
908
|
-
handler: (args, ctx) => listPosts(ctx.graphql,
|
|
860
|
+
handler: (args, ctx) => listPosts(ctx.graphql, args)
|
|
909
861
|
},
|
|
910
862
|
{
|
|
911
863
|
name: "get_post",
|
|
912
864
|
description: "Fetch a single post by slug or postId. Returns null if not found.",
|
|
913
865
|
inputSchema: getPostSchema,
|
|
914
|
-
handler: (args, ctx) => getPost(ctx.graphql,
|
|
866
|
+
handler: (args, ctx) => getPost(ctx.graphql, args)
|
|
915
867
|
},
|
|
916
868
|
{
|
|
917
869
|
name: "create_post",
|
|
918
|
-
description: "Create a new post. Title and slug are required. Body shape depends on format: tiptap=JSON node tree, markdown=source string, html=raw HTML string. Defaults to status=draft. Pass `metadata: { no_layout: true }` alongside format=html to publish the body as a bare HTML page with no theme chrome (the public route redirects to /
|
|
870
|
+
description: "Create a new post. Title and slug are required. Body shape depends on format: tiptap=JSON node tree, markdown=source string, html=raw HTML string. Defaults to status=draft. Pass `metadata: { no_layout: true }` alongside format=html to publish the body as a bare HTML page with no theme chrome (the public route redirects to /_/<slug>).",
|
|
919
871
|
inputSchema: createPostSchema,
|
|
920
|
-
handler: (args, ctx) => createPost(ctx.graphql,
|
|
872
|
+
handler: (args, ctx) => createPost(ctx.graphql, args)
|
|
921
873
|
},
|
|
922
874
|
{
|
|
923
875
|
name: "update_post",
|
|
924
876
|
description: "Update an existing post by postId. Only the fields you pass are changed. Tag list / publishedAt changes also update the PostTag denormalized index. Passing `metadata` REPLACES the existing object \u2014 call get_post first if you only want to add or change one key.",
|
|
925
877
|
inputSchema: updatePostSchema,
|
|
926
|
-
handler: (args, ctx) => updatePost(ctx.graphql,
|
|
878
|
+
handler: (args, ctx) => updatePost(ctx.graphql, args)
|
|
927
879
|
},
|
|
928
880
|
{
|
|
929
881
|
name: "delete_post",
|
|
930
882
|
description: "Delete a post by postId. Also drops associated PostTag index entries.",
|
|
931
883
|
inputSchema: deletePostSchema,
|
|
932
|
-
handler: (args, ctx) => deletePost(ctx.graphql,
|
|
884
|
+
handler: (args, ctx) => deletePost(ctx.graphql, args)
|
|
933
885
|
},
|
|
934
886
|
{
|
|
935
887
|
name: "upload_media",
|
|
936
888
|
description: "Upload a file to the site's media S3 bucket. Pass base64-encoded bytes; the server stores them verbatim under public/media/YYYY/MM/. Returns the public URL and Media record. The server does not transcode \u2014 pre-process (e.g. resize/webp) on the client.",
|
|
937
889
|
inputSchema: uploadMediaSchema,
|
|
938
|
-
handler: (args, ctx) => uploadMedia(ctx.graphql, ctx.storage(),
|
|
890
|
+
handler: (args, ctx) => uploadMedia(ctx.graphql, ctx.storage(), args)
|
|
939
891
|
},
|
|
940
892
|
{
|
|
941
893
|
name: "get_schema",
|
|
@@ -945,12 +897,11 @@ var tools = [
|
|
|
945
897
|
},
|
|
946
898
|
{
|
|
947
899
|
name: "upload_static_bundle",
|
|
948
|
-
description: "Create or replace a `format: 'static'` post in one shot. Pass a base64-encoded zip; the server unpacks it, validates every path + lints HTML/CSS/SVG for absolute path refs (bundles must be self-contained via relative paths), wipes the existing S3 prefix at public/static/<
|
|
900
|
+
description: "Create or replace a `format: 'static'` post in one shot. Pass a base64-encoded zip; the server unpacks it, validates every path + lints HTML/CSS/SVG for absolute path refs (bundles must be self-contained via relative paths), wipes the existing S3 prefix at public/static/<slug>/, uploads every file, and upserts the Post row with a manifest pointing at the entrypoint. Use this when you have the whole bundle to submit at once. For incremental edits use upload_static_file / delete_static_file followed by commit_static_post.",
|
|
949
901
|
inputSchema: uploadStaticBundleSchema,
|
|
950
902
|
handler: (args, ctx) => uploadStaticBundle(
|
|
951
903
|
ctx.graphql,
|
|
952
904
|
ctx.storage(),
|
|
953
|
-
ctx.defaultSiteId,
|
|
954
905
|
args
|
|
955
906
|
)
|
|
956
907
|
},
|
|
@@ -960,7 +911,6 @@ var tools = [
|
|
|
960
911
|
inputSchema: uploadStaticFileSchema,
|
|
961
912
|
handler: (args, ctx) => uploadStaticFile(
|
|
962
913
|
ctx.storage(),
|
|
963
|
-
ctx.defaultSiteId,
|
|
964
914
|
args
|
|
965
915
|
)
|
|
966
916
|
},
|
|
@@ -970,18 +920,16 @@ var tools = [
|
|
|
970
920
|
inputSchema: deleteStaticFileSchema,
|
|
971
921
|
handler: (args, ctx) => deleteStaticFile(
|
|
972
922
|
ctx.storage(),
|
|
973
|
-
ctx.defaultSiteId,
|
|
974
923
|
args
|
|
975
924
|
)
|
|
976
925
|
},
|
|
977
926
|
{
|
|
978
927
|
name: "commit_static_post",
|
|
979
|
-
description: 'Rebuild a static post\'s Post row from whatever is currently in its S3 prefix. Scans `public/static/<
|
|
928
|
+
description: 'Rebuild a static post\'s Post row from whatever is currently in its S3 prefix. Scans `public/static/<slug>/`, picks the entrypoint (default `index.html` or override), and upserts the Post with a fresh manifest (sorted file list + uploadedAt timestamp). Use this as the "save" step after a series of `upload_static_file` / `delete_static_file` calls. `title` is required when creating a brand-new post; on update, existing fields are preserved unless explicitly overridden.',
|
|
980
929
|
inputSchema: commitStaticPostSchema,
|
|
981
930
|
handler: (args, ctx) => commitStaticPost(
|
|
982
931
|
ctx.graphql,
|
|
983
932
|
ctx.storage(),
|
|
984
|
-
ctx.defaultSiteId,
|
|
985
933
|
args
|
|
986
934
|
)
|
|
987
935
|
}
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-IW52OUIR.js";
|
|
8
8
|
import {
|
|
9
9
|
tools
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DAR6AHSX.js";
|
|
11
11
|
import {
|
|
12
12
|
AwsCrc32,
|
|
13
13
|
__awaiter,
|
|
@@ -10895,10 +10895,6 @@ function parseArgs(argv) {
|
|
|
10895
10895
|
out.outputs = argv[++i2];
|
|
10896
10896
|
} else if (arg.startsWith("--outputs=")) {
|
|
10897
10897
|
out.outputs = arg.slice("--outputs=".length);
|
|
10898
|
-
} else if (arg === "--site-id") {
|
|
10899
|
-
out.defaultSiteId = argv[++i2];
|
|
10900
|
-
} else if (arg.startsWith("--site-id=")) {
|
|
10901
|
-
out.defaultSiteId = arg.slice("--site-id=".length);
|
|
10902
10898
|
}
|
|
10903
10899
|
}
|
|
10904
10900
|
return out;
|
|
@@ -10938,8 +10934,7 @@ async function loadConfig2(argv = process.argv.slice(2)) {
|
|
|
10938
10934
|
return {
|
|
10939
10935
|
outputs,
|
|
10940
10936
|
email: requireEnv("AMPLESS_MCP_EMAIL"),
|
|
10941
|
-
password: requireEnv("AMPLESS_MCP_PASSWORD")
|
|
10942
|
-
defaultSiteId: args.defaultSiteId ?? process.env.AMPLESS_MCP_SITE_ID ?? "default"
|
|
10937
|
+
password: requireEnv("AMPLESS_MCP_PASSWORD")
|
|
10943
10938
|
};
|
|
10944
10939
|
}
|
|
10945
10940
|
|
|
@@ -32181,8 +32176,7 @@ async function startServer(config2) {
|
|
|
32181
32176
|
};
|
|
32182
32177
|
const ctx = {
|
|
32183
32178
|
graphql,
|
|
32184
|
-
storage
|
|
32185
|
-
defaultSiteId: config2.defaultSiteId
|
|
32179
|
+
storage
|
|
32186
32180
|
};
|
|
32187
32181
|
const server = new Server(
|
|
32188
32182
|
{ name: "@ampless/mcp-server", version: "0.0.1" },
|
package/dist/tools/index.d.ts
CHANGED
package/dist/tools/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ampless/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-alpha.10",
|
|
4
4
|
"description": "MCP server for ampless — lets AI agents (Claude Desktop, Cursor, Claude Code) read and write posts and media via AppSync",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@aws-sdk/client-s3": "^3.1048.0",
|
|
37
37
|
"amazon-cognito-identity-js": "^6.3.12",
|
|
38
38
|
"fflate": "^0.8.2",
|
|
39
|
-
"ampless": "0.
|
|
39
|
+
"ampless": "1.0.0-alpha.9"
|
|
40
40
|
},
|
|
41
41
|
"keywords": [
|
|
42
42
|
"ampless",
|