@ampless/mcp-server 0.2.0-alpha.6 → 0.2.0-alpha.8

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.
@@ -1,6 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/tools/post-mapping.ts
4
+ import {
5
+ decodeAwsJson
6
+ } from "ampless";
4
7
  var POST_FIELDS = (
5
8
  /* GraphQL */
6
9
  `
@@ -15,22 +18,12 @@ var POST_FIELDS = (
15
18
  status
16
19
  publishedAt
17
20
  tags
21
+ metadata
18
22
  }
19
23
  `
20
24
  );
21
- function decodeBody(value) {
22
- if (typeof value !== "string") return value;
23
- try {
24
- return JSON.parse(value);
25
- } catch {
26
- return value;
27
- }
28
- }
29
- function encodeBody(value) {
30
- if (typeof value === "string") return value;
31
- return JSON.stringify(value ?? null);
32
- }
33
25
  function toCorePost(p) {
26
+ const metadata = decodeAwsJson(p.metadata);
34
27
  return {
35
28
  siteId: p.siteId,
36
29
  postId: p.postId,
@@ -38,10 +31,11 @@ function toCorePost(p) {
38
31
  title: p.title,
39
32
  excerpt: p.excerpt ?? void 0,
40
33
  format: p.format ?? "markdown",
41
- body: decodeBody(p.body),
34
+ body: decodeAwsJson(p.body),
42
35
  status: p.status ?? "draft",
43
36
  publishedAt: p.publishedAt ?? void 0,
44
- tags: (p.tags ?? []).filter((t) => typeof t === "string")
37
+ tags: (p.tags ?? []).filter((t) => typeof t === "string"),
38
+ metadata: metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata : void 0
45
39
  };
46
40
  }
47
41
 
@@ -135,7 +129,11 @@ async function getPost(client, defaultSiteId, args) {
135
129
  }
136
130
 
137
131
  // src/tools/create-post.ts
138
- import { composeSiteIdStatus, composeSiteIdSlug } from "ampless";
132
+ import {
133
+ composeSiteIdStatus,
134
+ composeSiteIdSlug,
135
+ encodeAwsJson
136
+ } from "ampless";
139
137
 
140
138
  // src/posttag.ts
141
139
  function entries(post) {
@@ -247,14 +245,29 @@ var createPostSchema = {
247
245
  postId: { type: "string", description: "Optional explicit id; auto-generated if omitted" },
248
246
  slug: { type: "string" },
249
247
  title: { type: "string" },
250
- format: { type: "string", enum: ["tiptap", "markdown", "html"] },
248
+ format: {
249
+ type: "string",
250
+ enum: ["tiptap", "markdown", "html"],
251
+ description: "tiptap = rich text JSON tree; markdown = source string (GFM extensions enabled); html = raw HTML string (rendered verbatim, no sanitization)."
252
+ },
251
253
  body: {
252
- description: "tiptap JSON (when format=tiptap), markdown source string, or raw HTML string"
254
+ description: "tiptap JSON (when format=tiptap), markdown source string (format=markdown), or raw HTML string (format=html)."
253
255
  },
254
256
  status: { type: "string", enum: ["draft", "published"], default: "draft" },
255
257
  excerpt: { type: "string" },
256
258
  publishedAt: { type: "string", description: "ISO 8601 timestamp; required when status=published" },
257
- tags: { type: "array", items: { type: "string" } }
259
+ tags: { type: "array", items: { type: "string" } },
260
+ metadata: {
261
+ 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.",
263
+ properties: {
264
+ no_layout: {
265
+ type: "boolean",
266
+ description: "Serve the post as bare HTML with no theme chrome. Only meaningful when format=html; ignored otherwise."
267
+ }
268
+ },
269
+ additionalProperties: true
270
+ }
258
271
  }
259
272
  };
260
273
  async function createPost(client, defaultSiteId, args) {
@@ -270,10 +283,11 @@ async function createPost(client, defaultSiteId, args) {
270
283
  title: args.title,
271
284
  excerpt: args.excerpt,
272
285
  format: args.format,
273
- body: encodeBody(args.body),
286
+ body: encodeAwsJson(args.body),
274
287
  status,
275
288
  publishedAt,
276
289
  tags: args.tags,
290
+ metadata: args.metadata !== void 0 ? encodeAwsJson(args.metadata) : void 0,
277
291
  // Denormalized GSI keys.
278
292
  siteIdStatus: composeSiteIdStatus(siteId, status),
279
293
  siteIdSlug: composeSiteIdSlug(siteId, args.slug)
@@ -285,7 +299,11 @@ async function createPost(client, defaultSiteId, args) {
285
299
  }
286
300
 
287
301
  // src/tools/update-post.ts
288
- import { composeSiteIdStatus as composeSiteIdStatus2, composeSiteIdSlug as composeSiteIdSlug2 } from "ampless";
302
+ import {
303
+ composeSiteIdStatus as composeSiteIdStatus2,
304
+ composeSiteIdSlug as composeSiteIdSlug2,
305
+ encodeAwsJson as encodeAwsJson2
306
+ } from "ampless";
289
307
  var MUTATION2 = (
290
308
  /* GraphQL */
291
309
  `
@@ -306,11 +324,26 @@ var updatePostSchema = {
306
324
  slug: { type: "string" },
307
325
  title: { type: "string" },
308
326
  excerpt: { type: "string" },
309
- format: { type: "string", enum: ["tiptap", "markdown", "html"] },
310
- body: { description: "tiptap JSON, markdown source, or raw HTML" },
327
+ format: {
328
+ type: "string",
329
+ enum: ["tiptap", "markdown", "html"],
330
+ description: "tiptap = rich text JSON tree; markdown = source string; html = raw HTML string (no sanitization)."
331
+ },
332
+ body: { description: "tiptap JSON, markdown source, or raw HTML string" },
311
333
  status: { type: "string", enum: ["draft", "published"] },
312
334
  publishedAt: { type: "string", description: "ISO 8601 timestamp" },
313
- tags: { type: "array", items: { type: "string" } }
335
+ tags: { type: "array", items: { type: "string" } },
336
+ metadata: {
337
+ 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.",
339
+ properties: {
340
+ no_layout: {
341
+ type: "boolean",
342
+ description: "Serve the post as bare HTML with no theme chrome. Only meaningful when format=html; ignored otherwise."
343
+ }
344
+ },
345
+ additionalProperties: true
346
+ }
314
347
  }
315
348
  };
316
349
  async function updatePost(client, defaultSiteId, args) {
@@ -321,10 +354,11 @@ async function updatePost(client, defaultSiteId, args) {
321
354
  if (args.title !== void 0) input.title = args.title;
322
355
  if (args.excerpt !== void 0) input.excerpt = args.excerpt;
323
356
  if (args.format !== void 0) input.format = args.format;
324
- if (args.body !== void 0) input.body = encodeBody(args.body);
357
+ if (args.body !== void 0) input.body = encodeAwsJson2(args.body);
325
358
  if (args.status !== void 0) input.status = args.status;
326
359
  if (args.publishedAt !== void 0) input.publishedAt = args.publishedAt;
327
360
  if (args.tags !== void 0) input.tags = args.tags;
361
+ if (args.metadata !== void 0) input.metadata = encodeAwsJson2(args.metadata);
328
362
  if (args.status !== void 0) {
329
363
  input.siteIdStatus = composeSiteIdStatus2(siteId, args.status);
330
364
  }
@@ -453,7 +487,11 @@ function getSchema() {
453
487
  },
454
488
  status: { type: "enum", values: ["draft", "published"], default: "draft" },
455
489
  publishedAt: { type: "datetime", description: "ISO 8601" },
456
- tags: { type: "string[]" }
490
+ tags: { type: "string[]" },
491
+ metadata: {
492
+ 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."
494
+ }
457
495
  }
458
496
  },
459
497
  {
@@ -486,7 +524,9 @@ function getSchema() {
486
524
  formats: ["tiptap", "markdown", "html"],
487
525
  notes: {
488
526
  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".',
489
- tiptapBody: 'When format=tiptap, body is the tiptap document JSON: { type: "doc", content: [...] }. The renderer expects this shape.'
527
+ 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 uploads currently only flow through the admin UI; the MCP `upload_media` tool writes to public/media/ and does not handle static bundles. Use the admin StaticUploader for now."
490
530
  }
491
531
  };
492
532
  }
@@ -507,13 +547,13 @@ var tools = [
507
547
  },
508
548
  {
509
549
  name: "create_post",
510
- 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.",
550
+ 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>).",
511
551
  inputSchema: createPostSchema,
512
552
  handler: (args, ctx) => createPost(ctx.graphql, ctx.defaultSiteId, args)
513
553
  },
514
554
  {
515
555
  name: "update_post",
516
- description: "Update an existing post by postId. Only the fields you pass are changed. Tag list / publishedAt changes also update the PostTag denormalized index.",
556
+ 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.",
517
557
  inputSchema: updatePostSchema,
518
558
  handler: (args, ctx) => updatePost(ctx.graphql, ctx.defaultSiteId, args)
519
559
  },
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-KM6F5QJH.js";
10
+ } from "./chunk-EGQ7Q3Y3.js";
11
11
  import {
12
12
  AwsCrc32,
13
13
  __awaiter,
@@ -40,10 +40,6 @@ declare const tools: ToolDefinition[];
40
40
  * Look up a tool definition by name and invoke its handler. Returns
41
41
  * `null` when no tool with that name is registered — callers should
42
42
  * surface that as a JSON-RPC "method not found" error.
43
- *
44
- * Shared between the stdio CLI (`src/index.ts`) and the HTTP transport
45
- * factory (`@ampless/admin/api/mcp` → `createMcpRoute`) so both routes
46
- * dispatch through the same code path.
47
43
  */
48
44
  declare function dispatchToolCall(name: string, args: Record<string, unknown>, ctx: ToolContext): Promise<unknown>;
49
45
  /**
@@ -3,7 +3,7 @@ import {
3
3
  dispatchToolCall,
4
4
  getTools,
5
5
  tools
6
- } from "../chunk-KM6F5QJH.js";
6
+ } from "../chunk-EGQ7Q3Y3.js";
7
7
  import "../chunk-LMMQX4CK.js";
8
8
  export {
9
9
  dispatchToolCall,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ampless/mcp-server",
3
- "version": "0.2.0-alpha.6",
3
+ "version": "0.2.0-alpha.8",
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",
@@ -35,7 +35,7 @@
35
35
  "@modelcontextprotocol/sdk": "^1.0.0",
36
36
  "@aws-sdk/client-s3": "^3.1048.0",
37
37
  "amazon-cognito-identity-js": "^6.3.12",
38
- "ampless": "0.2.0-alpha.6"
38
+ "ampless": "0.2.0-alpha.7"
39
39
  },
40
40
  "keywords": [
41
41
  "ampless",