@ampless/mcp-server 0.2.0-alpha.9 → 1.0.0-alpha.11

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, defaultSiteId, args = {}) {
71
- const siteId = args.siteId ?? defaultSiteId;
67
+ async function listPosts(client, args = {}) {
72
68
  const status = args.status ?? "all";
73
- const filter = { siteId: { eq: siteId } };
69
+ const filter = {};
74
70
  if (status !== "all") filter.status = { eq: status };
75
- const data = await client.query(QUERY, { filter, limit: args.limit ?? 20, nextToken: args.nextToken });
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($siteId: String!, $postId: ID!) {
88
- getPost(siteId: $siteId, postId: $postId) {
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, defaultSiteId, args) {
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, { siteId, postId: args.postId });
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: { siteId: { eq: siteId }, slug: { eq: args.slug } }
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
- siteIdTag: `${post.siteId}#${tag}`,
139
+ tag,
143
140
  publishedAtPostId: `${post.publishedAt}#${post.postId}`
144
141
  }));
145
142
  }
146
143
  function entryKey(e) {
147
- return `${e.siteIdTag}|${e.publishedAtPostId}`;
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
- siteIdTag
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
- siteIdTag
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
- siteIdTag
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: { siteIdTag: e.siteIdTag, publishedAtPostId: e.publishedAtPostId }
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
- siteIdTag: e.siteIdTag,
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
- siteIdTag: e.siteIdTag,
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,25 +253,29 @@ 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 /raw/<slug>); meaningful only with format=html. Other keys pass through unchanged for themes/plugins.",
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 (middleware rewrites /<slug> to the internal bare-HTML route); meaningful only with format=html. `cache` (auto|deep|hot) \u2014 override the per-post cache strategy. See `cacheStrategy` in get_schema.notes for details. Other keys pass through unchanged for themes/plugins.",
263
257
  properties: {
264
258
  no_layout: {
265
259
  type: "boolean",
266
260
  description: "Serve the post as bare HTML with no theme chrome. Only meaningful when format=html; ignored otherwise."
261
+ },
262
+ cache: {
263
+ type: "string",
264
+ enum: ["auto", "deep", "hot"],
265
+ default: "auto",
266
+ description: "Override the response Cache-Control strategy. 'auto' (default): no-store within `cms.config.cache.cooldownMs` of updatedAt, then `max-age=freshTtlSeconds`. 'deep': always `max-age=deepTtlSeconds`. 'hot': always no-store. Independent of no_layout; applies uniformly to themed, no_layout, and static posts."
267
267
  }
268
268
  },
269
269
  additionalProperties: true
270
270
  }
271
271
  }
272
272
  };
273
- async function createPost(client, defaultSiteId, args) {
274
- const siteId = args.siteId ?? defaultSiteId;
273
+ async function createPost(client, args) {
275
274
  const postId = args.postId ?? `post-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
276
275
  const status = args.status ?? "draft";
277
276
  const publishedAt = args.publishedAt ?? (status === "published" ? (/* @__PURE__ */ new Date()).toISOString() : void 0);
278
277
  const data = await client.query(MUTATION, {
279
278
  input: {
280
- siteId,
281
279
  postId,
282
280
  slug: args.slug,
283
281
  title: args.title,
@@ -287,10 +285,7 @@ async function createPost(client, defaultSiteId, args) {
287
285
  status,
288
286
  publishedAt,
289
287
  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)
288
+ metadata: args.metadata !== void 0 ? encodeAwsJson(args.metadata) : void 0
294
289
  }
295
290
  });
296
291
  const created = toCorePost(data.createPost);
@@ -300,8 +295,6 @@ async function createPost(client, defaultSiteId, args) {
300
295
 
301
296
  // src/tools/update-post.ts
302
297
  import {
303
- composeSiteIdStatus as composeSiteIdStatus2,
304
- composeSiteIdSlug as composeSiteIdSlug2,
305
298
  encodeAwsJson as encodeAwsJson2
306
299
  } from "ampless";
307
300
  var MUTATION2 = (
@@ -320,7 +313,6 @@ var updatePostSchema = {
320
313
  required: ["postId"],
321
314
  properties: {
322
315
  postId: { type: "string" },
323
- siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
324
316
  slug: { type: "string" },
325
317
  title: { type: "string" },
326
318
  excerpt: { type: "string" },
@@ -335,21 +327,26 @@ var updatePostSchema = {
335
327
  tags: { type: "array", items: { type: "string" } },
336
328
  metadata: {
337
329
  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 /raw/<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.",
330
+ 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 (middleware rewrites /<slug> to the internal bare-HTML route); meaningful only with format=html. `cache` (auto|deep|hot) \u2014 override the per-post cache strategy. See `cacheStrategy` in get_schema.notes for details. Passing metadata replaces the existing metadata object \u2014 read the current post first if you only want to add a key.",
339
331
  properties: {
340
332
  no_layout: {
341
333
  type: "boolean",
342
334
  description: "Serve the post as bare HTML with no theme chrome. Only meaningful when format=html; ignored otherwise."
335
+ },
336
+ cache: {
337
+ type: "string",
338
+ enum: ["auto", "deep", "hot"],
339
+ default: "auto",
340
+ description: "Override the response Cache-Control strategy. 'auto' (default): no-store within `cms.config.cache.cooldownMs` of updatedAt, then `max-age=freshTtlSeconds`. 'deep': always `max-age=deepTtlSeconds`. 'hot': always no-store. Independent of no_layout; applies uniformly to themed, no_layout, and static posts."
343
341
  }
344
342
  },
345
343
  additionalProperties: true
346
344
  }
347
345
  }
348
346
  };
349
- async function updatePost(client, defaultSiteId, args) {
350
- const siteId = args.siteId ?? defaultSiteId;
351
- const oldPost = await getPost(client, defaultSiteId, { postId: args.postId, siteId });
352
- const input = { siteId, postId: args.postId };
347
+ async function updatePost(client, args) {
348
+ const oldPost = await getPost(client, { postId: args.postId });
349
+ const input = { postId: args.postId };
353
350
  if (args.slug !== void 0) input.slug = args.slug;
354
351
  if (args.title !== void 0) input.title = args.title;
355
352
  if (args.excerpt !== void 0) input.excerpt = args.excerpt;
@@ -359,12 +356,6 @@ async function updatePost(client, defaultSiteId, args) {
359
356
  if (args.publishedAt !== void 0) input.publishedAt = args.publishedAt;
360
357
  if (args.tags !== void 0) input.tags = args.tags;
361
358
  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
359
  const data = await client.query(MUTATION2, { input });
369
360
  const updated = toCorePost(data.updatePost);
370
361
  await syncPostTags(client, updated, oldPost);
@@ -377,7 +368,6 @@ var MUTATION3 = (
377
368
  `
378
369
  mutation DeletePost($input: DeletePostInput!) {
379
370
  deletePost(input: $input) {
380
- siteId
381
371
  postId
382
372
  }
383
373
  }
@@ -387,17 +377,15 @@ var deletePostSchema = {
387
377
  type: "object",
388
378
  required: ["postId"],
389
379
  properties: {
390
- postId: { type: "string" },
391
- siteId: { type: "string", description: 'Site identifier (defaults to "default")' }
380
+ postId: { type: "string" }
392
381
  }
393
382
  };
394
- async function deletePost(client, defaultSiteId, args) {
395
- const siteId = args.siteId ?? defaultSiteId;
396
- const oldPost = await getPost(client, defaultSiteId, { postId: args.postId, siteId });
383
+ async function deletePost(client, args) {
384
+ const oldPost = await getPost(client, { postId: args.postId });
397
385
  if (oldPost) {
398
386
  await syncPostTags(client, { ...oldPost, status: "draft" }, oldPost);
399
387
  }
400
- const data = await client.query(MUTATION3, { input: { siteId, postId: args.postId } });
388
+ const data = await client.query(MUTATION3, { input: { postId: args.postId } });
401
389
  return { deleted: data.deletePost };
402
390
  }
403
391
 
@@ -418,7 +406,6 @@ var MUTATION4 = (
418
406
  `
419
407
  mutation CreateMedia($input: CreateMediaInput!) {
420
408
  createMedia(input: $input) {
421
- siteId
422
409
  mediaId
423
410
  src
424
411
  mimeType
@@ -432,7 +419,6 @@ var uploadMediaSchema = {
432
419
  type: "object",
433
420
  required: ["filename", "mimeType", "base64Data"],
434
421
  properties: {
435
- siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
436
422
  filename: { type: "string", description: "Original filename; sanitized server-side" },
437
423
  mimeType: {
438
424
  type: "string",
@@ -444,15 +430,13 @@ var uploadMediaSchema = {
444
430
  }
445
431
  }
446
432
  };
447
- async function uploadMedia(graphql, storage, defaultSiteId, args) {
448
- const siteId = args.siteId ?? defaultSiteId;
433
+ async function uploadMedia(graphql, storage, args) {
449
434
  const body = Buffer.from(args.base64Data, "base64");
450
435
  const key = buildMediaKey(args.filename);
451
436
  const url = await storage.putObject(key, body, args.mimeType);
452
437
  const mediaId = `media-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
453
438
  const data = await graphql.query(MUTATION4, {
454
439
  input: {
455
- siteId,
456
440
  mediaId,
457
441
  src: key,
458
442
  mimeType: args.mimeType,
@@ -473,11 +457,10 @@ function getSchema() {
473
457
  contentTypes: [
474
458
  {
475
459
  name: "post",
476
- identifier: ["siteId", "postId"],
460
+ identifier: ["postId"],
477
461
  fields: {
478
- siteId: { type: "string", required: true },
479
462
  postId: { type: "string", required: true, description: "auto-generated if omitted on create" },
480
- slug: { type: "string", required: true, description: "URL slug, unique per site" },
463
+ slug: { type: "string", required: true, description: "URL slug, unique" },
481
464
  title: { type: "string", required: true },
482
465
  excerpt: { type: "string" },
483
466
  format: { type: "enum", values: ["tiptap", "markdown", "html"], required: true },
@@ -490,15 +473,14 @@ function getSchema() {
490
473
  tags: { type: "string[]" },
491
474
  metadata: {
492
475
  type: "json",
493
- description: "Free-form per-post key/value bag. Reserved well-known keys (owned by ampless): `no_layout` (boolean). Other keys pass through unchanged for themes/plugins."
476
+ description: "Free-form per-post key/value bag. Reserved well-known keys (owned by ampless): `no_layout` (boolean), `cache` (auto|deep|hot). Other keys pass through unchanged for themes/plugins."
494
477
  }
495
478
  }
496
479
  },
497
480
  {
498
481
  name: "page",
499
- identifier: ["siteId", "pageId"],
482
+ identifier: ["pageId"],
500
483
  fields: {
501
- siteId: { type: "string", required: true },
502
484
  pageId: { type: "string", required: true },
503
485
  slug: { type: "string", required: true },
504
486
  title: { type: "string", required: true },
@@ -510,9 +492,8 @@ function getSchema() {
510
492
  },
511
493
  {
512
494
  name: "media",
513
- identifier: ["siteId", "mediaId"],
495
+ identifier: ["mediaId"],
514
496
  fields: {
515
- siteId: { type: "string", required: true },
516
497
  mediaId: { type: "string", required: true },
517
498
  src: { type: "string", required: true, description: "S3 key" },
518
499
  mimeType: { type: "string", required: true },
@@ -525,8 +506,9 @@ function getSchema() {
525
506
  notes: {
526
507
  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
508
  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> 302-redirects to /raw/<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).",
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/<siteId>/<slug>/. 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."
509
+ noLayout: "metadata.no_layout=true serves the post as bare HTML with no theme chrome \u2014 middleware rewrites the public /<slug> request to the internal bare-HTML handler, which 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).",
510
+ 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 (middleware rewrites both to an internal static-bundle handler). 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.",
511
+ cacheStrategy: "metadata.cache controls the response Cache-Control header (set by middleware). Values:\n - 'auto' (default): cooldown by edit time. Posts updated within cms.config.cache.cooldownMs (default 1h) emit `public, max-age=0, must-revalidate, s-maxage=0` so editors see fresh content immediately; older posts emit `public, max-age=<freshTtlSeconds>, s-maxage=<freshTtlSeconds>` (default 300 sec / 5 min).\n - 'deep': always `public, max-age=<deepTtlSeconds>, s-maxage=<deepTtlSeconds>` (default 3600 sec / 1 hour). Use for posts whose content is fixed for the foreseeable future.\n - 'hot': always `public, max-age=0, must-revalidate, s-maxage=0`. Use for rapidly-evolving posts or posts whose body is computed per request.\nIndependent of metadata.no_layout and post.format \u2014 applies uniformly to themed, no_layout, and static posts. Override the defaults per project via `cms.config.cache` ({ cooldownMs, freshTtlSeconds, deepTtlSeconds })."
530
512
  }
531
513
  };
532
514
  }
@@ -576,8 +558,6 @@ function decodeUtf8(data) {
576
558
 
577
559
  // src/tools/upsert-static-post.ts
578
560
  import {
579
- composeSiteIdStatus as composeSiteIdStatus3,
580
- composeSiteIdSlug as composeSiteIdSlug3,
581
561
  encodeAwsJson as encodeAwsJson3
582
562
  } from "ampless";
583
563
  var CREATE_MUTATION = (
@@ -602,21 +582,17 @@ var UPDATE_MUTATION = (
602
582
  }
603
583
  `
604
584
  );
605
- async function upsertStaticPost(graphql, siteId, slug, body, fields) {
606
- const existing = await getPost(graphql, siteId, { siteId, slug });
585
+ async function upsertStaticPost(graphql, slug, body, fields) {
586
+ const existing = await getPost(graphql, { slug });
607
587
  if (existing) {
608
588
  const input2 = {
609
- siteId,
610
589
  postId: existing.postId,
611
590
  format: "static",
612
591
  body: encodeAwsJson3(body)
613
592
  };
614
593
  if (fields.title !== void 0) input2.title = fields.title;
615
594
  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
- }
595
+ if (fields.status !== void 0) input2.status = fields.status;
620
596
  if (fields.publishedAt !== void 0) input2.publishedAt = fields.publishedAt;
621
597
  if (fields.tags !== void 0) input2.tags = fields.tags;
622
598
  if (fields.metadata !== void 0) {
@@ -636,15 +612,12 @@ async function upsertStaticPost(graphql, siteId, slug, body, fields) {
636
612
  const publishedAt = fields.publishedAt ?? (status === "published" ? (/* @__PURE__ */ new Date()).toISOString() : void 0);
637
613
  const postId = fields.postId ?? `post-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
638
614
  const input = {
639
- siteId,
640
615
  postId,
641
616
  slug,
642
617
  title: fields.title,
643
618
  format: "static",
644
619
  body: encodeAwsJson3(body),
645
- status,
646
- siteIdStatus: composeSiteIdStatus3(siteId, status),
647
- siteIdSlug: composeSiteIdSlug3(siteId, slug)
620
+ status
648
621
  };
649
622
  if (fields.excerpt !== void 0) input.excerpt = fields.excerpt;
650
623
  if (publishedAt !== void 0) input.publishedAt = publishedAt;
@@ -661,7 +634,6 @@ var uploadStaticBundleSchema = {
661
634
  type: "object",
662
635
  required: ["slug", "title", "zipBase64"],
663
636
  properties: {
664
- siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
665
637
  postId: {
666
638
  type: "string",
667
639
  description: "Optional explicit Post id when creating a new post. Ignored if a post at this slug already exists."
@@ -687,8 +659,7 @@ var uploadStaticBundleSchema = {
687
659
  }
688
660
  }
689
661
  };
690
- async function uploadStaticBundle(graphql, storage, defaultSiteId, args) {
691
- const siteId = args.siteId ?? defaultSiteId;
662
+ async function uploadStaticBundle(graphql, storage, args) {
692
663
  const slug = args.slug;
693
664
  const zipBytes = Buffer.from(args.zipBase64, "base64");
694
665
  if (zipBytes.length === 0) {
@@ -715,7 +686,7 @@ async function uploadStaticBundle(graphql, storage, defaultSiteId, args) {
715
686
  `upload_static_bundle: entrypoint "${entrypoint}" is not present in the bundle.`
716
687
  );
717
688
  }
718
- const prefix = bundlePrefix(siteId, slug);
689
+ const prefix = bundlePrefix(slug);
719
690
  const existing = await storage.listObjects(prefix).catch((err) => {
720
691
  console.error("[upload_static_bundle] listObjects failed (proceeding)", err);
721
692
  return [];
@@ -733,7 +704,7 @@ async function uploadStaticBundle(graphql, storage, defaultSiteId, args) {
733
704
  files: files.map((f) => f.path).sort(),
734
705
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
735
706
  };
736
- const { post } = await upsertStaticPost(graphql, siteId, slug, body, {
707
+ const { post } = await upsertStaticPost(graphql, slug, body, {
737
708
  title: args.title,
738
709
  postId: args.postId,
739
710
  status: args.status,
@@ -757,8 +728,7 @@ var uploadStaticFileSchema = {
757
728
  type: "object",
758
729
  required: ["slug", "filename", "base64Data"],
759
730
  properties: {
760
- siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
761
- slug: { type: "string", description: "Bundle slug. Files land at public/static/<siteId>/<slug>/<filename>." },
731
+ slug: { type: "string", description: "Bundle slug. Files land at public/static/<slug>/<filename>." },
762
732
  filename: {
763
733
  type: "string",
764
734
  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 +743,7 @@ var uploadStaticFileSchema = {
773
743
  }
774
744
  }
775
745
  };
776
- async function uploadStaticFile(storage, defaultSiteId, args) {
777
- const siteId = args.siteId ?? defaultSiteId;
746
+ async function uploadStaticFile(storage, args) {
778
747
  const filename = args.filename;
779
748
  const reason = validateBundlePath2(filename);
780
749
  if (reason) {
@@ -795,7 +764,7 @@ async function uploadStaticFile(storage, defaultSiteId, args) {
795
764
  }
796
765
  }
797
766
  const contentType = args.contentType ?? mimeTypeFor2(filename);
798
- const key = `${bundlePrefix2(siteId, args.slug)}${filename}`;
767
+ const key = `${bundlePrefix2(args.slug)}${filename}`;
799
768
  const url = await storage.putObject(key, body, contentType);
800
769
  return { key, url, size: body.length };
801
770
  }
@@ -806,7 +775,6 @@ var deleteStaticFileSchema = {
806
775
  type: "object",
807
776
  required: ["slug", "filename"],
808
777
  properties: {
809
- siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
810
778
  slug: { type: "string" },
811
779
  filename: {
812
780
  type: "string",
@@ -814,13 +782,12 @@ var deleteStaticFileSchema = {
814
782
  }
815
783
  }
816
784
  };
817
- async function deleteStaticFile(storage, defaultSiteId, args) {
818
- const siteId = args.siteId ?? defaultSiteId;
785
+ async function deleteStaticFile(storage, args) {
819
786
  const reason = validateBundlePath3(args.filename);
820
787
  if (reason) {
821
788
  throw new Error(`delete_static_file: invalid filename "${args.filename}" (${reason})`);
822
789
  }
823
- const prefix = bundlePrefix3(siteId, args.slug);
790
+ const prefix = bundlePrefix3(args.slug);
824
791
  const key = `${prefix}${args.filename}`;
825
792
  const existing = await storage.listObjects(key);
826
793
  const found = existing.some((o) => o.key === key);
@@ -840,7 +807,6 @@ var commitStaticPostSchema = {
840
807
  type: "object",
841
808
  required: ["slug"],
842
809
  properties: {
843
- siteId: { type: "string", description: 'Site identifier (defaults to "default")' },
844
810
  slug: { type: "string" },
845
811
  title: {
846
812
  type: "string",
@@ -865,10 +831,9 @@ var commitStaticPostSchema = {
865
831
  }
866
832
  }
867
833
  };
868
- async function commitStaticPost(graphql, storage, defaultSiteId, args) {
869
- const siteId = args.siteId ?? defaultSiteId;
834
+ async function commitStaticPost(graphql, storage, args) {
870
835
  const slug = args.slug;
871
- const prefix = bundlePrefix4(siteId, slug);
836
+ const prefix = bundlePrefix4(slug);
872
837
  const objects = await storage.listObjects(prefix);
873
838
  if (objects.length === 0) {
874
839
  throw new Error(
@@ -887,7 +852,7 @@ async function commitStaticPost(graphql, storage, defaultSiteId, args) {
887
852
  files: relPaths,
888
853
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
889
854
  };
890
- const { post, created } = await upsertStaticPost(graphql, siteId, slug, body, {
855
+ const { post, created } = await upsertStaticPost(graphql, slug, body, {
891
856
  title: args.title,
892
857
  postId: args.postId,
893
858
  excerpt: args.excerpt,
@@ -905,37 +870,37 @@ var tools = [
905
870
  name: "list_posts",
906
871
  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
872
  inputSchema: listPostsSchema,
908
- handler: (args, ctx) => listPosts(ctx.graphql, ctx.defaultSiteId, args)
873
+ handler: (args, ctx) => listPosts(ctx.graphql, args)
909
874
  },
910
875
  {
911
876
  name: "get_post",
912
877
  description: "Fetch a single post by slug or postId. Returns null if not found.",
913
878
  inputSchema: getPostSchema,
914
- handler: (args, ctx) => getPost(ctx.graphql, ctx.defaultSiteId, args)
879
+ handler: (args, ctx) => getPost(ctx.graphql, args)
915
880
  },
916
881
  {
917
882
  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 /raw/<slug>).",
883
+ 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 (middleware rewrites the /<slug> request to the internal bare-HTML handler). Pass `metadata: { cache: "deep" | "hot" }` to override the default cooldown-based cache strategy \u2014 see get_schema.notes.cacheStrategy for details.',
919
884
  inputSchema: createPostSchema,
920
- handler: (args, ctx) => createPost(ctx.graphql, ctx.defaultSiteId, args)
885
+ handler: (args, ctx) => createPost(ctx.graphql, args)
921
886
  },
922
887
  {
923
888
  name: "update_post",
924
889
  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
890
  inputSchema: updatePostSchema,
926
- handler: (args, ctx) => updatePost(ctx.graphql, ctx.defaultSiteId, args)
891
+ handler: (args, ctx) => updatePost(ctx.graphql, args)
927
892
  },
928
893
  {
929
894
  name: "delete_post",
930
895
  description: "Delete a post by postId. Also drops associated PostTag index entries.",
931
896
  inputSchema: deletePostSchema,
932
- handler: (args, ctx) => deletePost(ctx.graphql, ctx.defaultSiteId, args)
897
+ handler: (args, ctx) => deletePost(ctx.graphql, args)
933
898
  },
934
899
  {
935
900
  name: "upload_media",
936
901
  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
902
  inputSchema: uploadMediaSchema,
938
- handler: (args, ctx) => uploadMedia(ctx.graphql, ctx.storage(), ctx.defaultSiteId, args)
903
+ handler: (args, ctx) => uploadMedia(ctx.graphql, ctx.storage(), args)
939
904
  },
940
905
  {
941
906
  name: "get_schema",
@@ -945,12 +910,11 @@ var tools = [
945
910
  },
946
911
  {
947
912
  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/<siteId>/<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.",
913
+ 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
914
  inputSchema: uploadStaticBundleSchema,
950
915
  handler: (args, ctx) => uploadStaticBundle(
951
916
  ctx.graphql,
952
917
  ctx.storage(),
953
- ctx.defaultSiteId,
954
918
  args
955
919
  )
956
920
  },
@@ -960,7 +924,6 @@ var tools = [
960
924
  inputSchema: uploadStaticFileSchema,
961
925
  handler: (args, ctx) => uploadStaticFile(
962
926
  ctx.storage(),
963
- ctx.defaultSiteId,
964
927
  args
965
928
  )
966
929
  },
@@ -970,18 +933,16 @@ var tools = [
970
933
  inputSchema: deleteStaticFileSchema,
971
934
  handler: (args, ctx) => deleteStaticFile(
972
935
  ctx.storage(),
973
- ctx.defaultSiteId,
974
936
  args
975
937
  )
976
938
  },
977
939
  {
978
940
  name: "commit_static_post",
979
- description: 'Rebuild a static post\'s Post row from whatever is currently in its S3 prefix. Scans `public/static/<siteId>/<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.',
941
+ 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
942
  inputSchema: commitStaticPostSchema,
981
943
  handler: (args, ctx) => commitStaticPost(
982
944
  ctx.graphql,
983
945
  ctx.storage(),
984
- ctx.defaultSiteId,
985
946
  args
986
947
  )
987
948
  }
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-4PTNHZO4.js";
10
+ } from "./chunk-K4GTND6P.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" },
@@ -48,7 +48,6 @@ interface StorageClient {
48
48
  interface ToolContext {
49
49
  graphql: GraphqlClient;
50
50
  storage: () => StorageClient;
51
- defaultSiteId: string;
52
51
  }
53
52
 
54
53
  /**
@@ -5,7 +5,7 @@ import {
5
5
  extractZipFromBuffer,
6
6
  getTools,
7
7
  tools
8
- } from "../chunk-4PTNHZO4.js";
8
+ } from "../chunk-K4GTND6P.js";
9
9
  import "../chunk-LMMQX4CK.js";
10
10
  export {
11
11
  decodeUtf8,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ampless/mcp-server",
3
- "version": "0.2.0-alpha.9",
3
+ "version": "1.0.0-alpha.11",
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.2.0-alpha.8"
39
+ "ampless": "1.0.0-alpha.10"
40
40
  },
41
41
  "keywords": [
42
42
  "ampless",