@ampless/mcp-server 1.0.0-alpha.19 → 1.0.0-alpha.20

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/dist/index.d.ts CHANGED
@@ -33,13 +33,29 @@ interface StorageObject {
33
33
  /** ISO 8601 timestamp of the last write, when the backend supplies it. */
34
34
  lastModified?: string;
35
35
  }
36
+ interface PutObjectResult {
37
+ /**
38
+ * Public URL of the stored object (same format both routes use:
39
+ * https://{bucket}.s3.{region}.amazonaws.com/{key}).
40
+ */
41
+ url: string;
42
+ /**
43
+ * S3 ETag of the stored bytes when the underlying client surfaces
44
+ * it. Captured at upload time so the Media DynamoDB row can record
45
+ * it and the media-proxy route can later passthrough it as a
46
+ * response header (enabling conditional GETs from CDN clients).
47
+ * Optional because not all `StorageClient` impls have access to
48
+ * the raw S3 response.
49
+ */
50
+ etag?: string;
51
+ }
36
52
  interface StorageClient {
37
53
  /**
38
54
  * Upload `body` to the bucket at `key` with `contentType`. Returns
39
- * the public URL of the stored object (same format both routes use:
40
- * https://{bucket}.s3.{region}.amazonaws.com/{key}).
55
+ * the public URL and (when available) the S3 ETag of the stored
56
+ * object.
41
57
  */
42
- putObject(key: string, body: Uint8Array, contentType: string): Promise<string>;
58
+ putObject(key: string, body: Uint8Array, contentType: string): Promise<PutObjectResult>;
43
59
  /**
44
60
  * Remove the object at `key`. Implementations should treat a missing
45
61
  * key as success (S3 DeleteObject is idempotent by default).
package/dist/index.js CHANGED
@@ -288,6 +288,9 @@ async function deletePost(client, args) {
288
288
  return { deleted: data.deletePost };
289
289
  }
290
290
 
291
+ // src/tools/upload-media.ts
292
+ import { encodeAwsJson as encodeAwsJson3 } from "ampless";
293
+
291
294
  // src/tools/media-key.ts
292
295
  function sanitizeName(name) {
293
296
  return name.replace(/[\x00-\x1f\x7f]/g, "").replace(/[\\/:*?"<>|]/g, "_").replace(/\s+/g, "_").replace(/^\.+/, "_").slice(0, 200) || "upload";
@@ -310,6 +313,7 @@ var MUTATION4 = (
310
313
  mimeType
311
314
  size
312
315
  delivery
316
+ metadata
313
317
  }
314
318
  }
315
319
  `
@@ -332,7 +336,9 @@ var uploadMediaSchema = {
332
336
  async function uploadMedia(graphql, storage, args) {
333
337
  const body = Buffer.from(args.base64Data, "base64");
334
338
  const key = buildMediaKey(args.filename);
335
- const url = await storage.putObject(key, body, args.mimeType);
339
+ const putResult = await storage.putObject(key, body, args.mimeType);
340
+ const metadata = {};
341
+ if (putResult.etag) metadata.etag = putResult.etag;
336
342
  const mediaId = `media-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
337
343
  const data = await graphql.query(MUTATION4, {
338
344
  input: {
@@ -340,10 +346,11 @@ async function uploadMedia(graphql, storage, args) {
340
346
  src: key,
341
347
  mimeType: args.mimeType,
342
348
  size: body.length,
343
- delivery: "nextjs"
349
+ delivery: "nextjs",
350
+ metadata: encodeAwsJson3(metadata)
344
351
  }
345
352
  });
346
- return { media: data.createMedia, url };
353
+ return { media: data.createMedia, url: putResult.url };
347
354
  }
348
355
 
349
356
  // src/tools/get-schema.ts
@@ -939,7 +946,7 @@ function decodeUtf8(data) {
939
946
 
940
947
  // src/tools/upsert-static-post.ts
941
948
  import {
942
- encodeAwsJson as encodeAwsJson3
949
+ encodeAwsJson as encodeAwsJson4
943
950
  } from "ampless";
944
951
  var CREATE_MUTATION = (
945
952
  /* GraphQL */
@@ -969,7 +976,7 @@ async function upsertStaticPost(graphql, slug, body, fields) {
969
976
  const input2 = {
970
977
  postId: existing.postId,
971
978
  format: "static",
972
- body: encodeAwsJson3(body)
979
+ body: encodeAwsJson4(body)
973
980
  };
974
981
  if (fields.title !== void 0) input2.title = fields.title;
975
982
  if (fields.excerpt !== void 0) input2.excerpt = fields.excerpt;
@@ -977,7 +984,7 @@ async function upsertStaticPost(graphql, slug, body, fields) {
977
984
  if (fields.publishedAt !== void 0) input2.publishedAt = fields.publishedAt;
978
985
  if (fields.tags !== void 0) input2.tags = fields.tags;
979
986
  if (fields.metadata !== void 0) {
980
- input2.metadata = encodeAwsJson3(fields.metadata);
987
+ input2.metadata = encodeAwsJson4(fields.metadata);
981
988
  }
982
989
  const data2 = await graphql.query(UPDATE_MUTATION, { input: input2 });
983
990
  const updated = toCorePost(data2.updatePost);
@@ -996,13 +1003,13 @@ async function upsertStaticPost(graphql, slug, body, fields) {
996
1003
  slug,
997
1004
  title: fields.title,
998
1005
  format: "static",
999
- body: encodeAwsJson3(body),
1006
+ body: encodeAwsJson4(body),
1000
1007
  status
1001
1008
  };
1002
1009
  if (fields.excerpt !== void 0) input.excerpt = fields.excerpt;
1003
1010
  if (publishedAt !== void 0) input.publishedAt = publishedAt;
1004
1011
  if (fields.tags !== void 0) input.tags = fields.tags;
1005
- if (fields.metadata !== void 0) input.metadata = encodeAwsJson3(fields.metadata);
1012
+ if (fields.metadata !== void 0) input.metadata = encodeAwsJson4(fields.metadata);
1006
1013
  const data = await graphql.query(CREATE_MUTATION, { input });
1007
1014
  const created = toCorePost(data.createPost);
1008
1015
  return { post: created, created: true };
@@ -1074,8 +1081,11 @@ async function uploadStaticBundle(graphql, storage, args) {
1074
1081
  await storage.deleteObject(obj.key);
1075
1082
  }
1076
1083
  let uploadedFiles = 0;
1084
+ const filesMeta = {};
1077
1085
  for (const f of files) {
1078
- await storage.putObject(`${prefix}${f.path}`, f.data, mimeTypeFor(f.path));
1086
+ const mimeType = mimeTypeFor(f.path);
1087
+ await storage.putObject(`${prefix}${f.path}`, f.data, mimeType);
1088
+ filesMeta[f.path] = { size: f.data.byteLength, mimeType };
1079
1089
  uploadedFiles += 1;
1080
1090
  }
1081
1091
  const body = {
@@ -1083,6 +1093,10 @@ async function uploadStaticBundle(graphql, storage, args) {
1083
1093
  files: files.map((f) => f.path).sort(),
1084
1094
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
1085
1095
  };
1096
+ const mergedMetadata = {
1097
+ ...args.metadata ?? {},
1098
+ files: filesMeta
1099
+ };
1086
1100
  const { post } = await upsertStaticPost(graphql, slug, body, {
1087
1101
  title: args.title,
1088
1102
  postId: args.postId,
@@ -1090,7 +1104,7 @@ async function uploadStaticBundle(graphql, storage, args) {
1090
1104
  publishedAt: args.publishedAt,
1091
1105
  excerpt: args.excerpt,
1092
1106
  tags: args.tags,
1093
- metadata: args.metadata
1107
+ metadata: mergedMetadata
1094
1108
  });
1095
1109
  return { post, bundle: body, uploadedFiles };
1096
1110
  }
@@ -1144,8 +1158,8 @@ async function uploadStaticFile(storage, args) {
1144
1158
  }
1145
1159
  const contentType = args.contentType ?? mimeTypeFor2(filename);
1146
1160
  const key = `${bundlePrefix2(args.slug)}${filename}`;
1147
- const url = await storage.putObject(key, body, contentType);
1148
- return { key, url, size: body.length };
1161
+ const { url } = await storage.putObject(key, body, contentType);
1162
+ return { key, url, size: body.length, contentType };
1149
1163
  }
1150
1164
 
1151
1165
  // src/tools/delete-static-file.ts
@@ -1180,6 +1194,7 @@ async function deleteStaticFile(storage, args) {
1180
1194
  // src/tools/commit-static-post.ts
1181
1195
  import {
1182
1196
  bundlePrefix as bundlePrefix4,
1197
+ mimeTypeFor as mimeTypeFor3,
1183
1198
  pickDefaultEntrypoint as pickDefaultEntrypoint2
1184
1199
  } from "ampless";
1185
1200
  var commitStaticPostSchema = {
@@ -1219,7 +1234,15 @@ async function commitStaticPost(graphql, storage, args) {
1219
1234
  `commit_static_post: no files found under "${prefix}". Upload at least one file via upload_static_file or upload_static_bundle before committing.`
1220
1235
  );
1221
1236
  }
1222
- const relPaths = objects.map((o) => o.key.slice(prefix.length)).filter((p) => p !== "").sort();
1237
+ const filesMeta = {};
1238
+ const relPaths = [];
1239
+ for (const obj of objects) {
1240
+ const rel = obj.key.slice(prefix.length);
1241
+ if (rel === "") continue;
1242
+ relPaths.push(rel);
1243
+ filesMeta[rel] = { size: obj.size, mimeType: mimeTypeFor3(rel) };
1244
+ }
1245
+ relPaths.sort();
1223
1246
  const entrypoint = args.entrypoint ?? pickDefaultEntrypoint2(relPaths.map((p) => ({ path: p })));
1224
1247
  if (!relPaths.includes(entrypoint)) {
1225
1248
  throw new Error(
@@ -1231,6 +1254,10 @@ async function commitStaticPost(graphql, storage, args) {
1231
1254
  files: relPaths,
1232
1255
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
1233
1256
  };
1257
+ const mergedMetadata = {
1258
+ ...args.metadata ?? {},
1259
+ files: filesMeta
1260
+ };
1234
1261
  const { post, created } = await upsertStaticPost(graphql, slug, body, {
1235
1262
  title: args.title,
1236
1263
  postId: args.postId,
@@ -1238,7 +1265,7 @@ async function commitStaticPost(graphql, storage, args) {
1238
1265
  status: args.status,
1239
1266
  publishedAt: args.publishedAt,
1240
1267
  tags: args.tags,
1241
- metadata: args.metadata
1268
+ metadata: mergedMetadata
1242
1269
  });
1243
1270
  return { post, bundle: body, created };
1244
1271
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ampless/mcp-server",
3
- "version": "1.0.0-alpha.19",
3
+ "version": "1.0.0-alpha.20",
4
4
  "description": "MCP tool registry shared by @ampless/backend mcp-handler Lambda. Installed transitively via @ampless/admin / @ampless/backend; no direct install needed.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -26,7 +26,7 @@
26
26
  "bugs": "https://github.com/heavymoons/ampless/issues",
27
27
  "dependencies": {
28
28
  "fflate": "^0.8.3",
29
- "ampless": "1.0.0-alpha.15"
29
+ "ampless": "1.0.0-alpha.16"
30
30
  },
31
31
  "keywords": [
32
32
  "ampless",