@ampless/mcp-server 1.0.0-alpha.54 → 1.0.0-alpha.56

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/README.ja.md CHANGED
@@ -62,7 +62,7 @@ import type { ToolDefinition, ToolContext, ResolvedSite } from '@ampless/mcp-ser
62
62
 
63
63
  | ツール | role | 説明 |
64
64
  |---|---|---|
65
- | `list_posts` | reader | オプションの status フィルターとページネーション付きで投稿一覧を取得 |
65
+ | `list_posts` | reader | 投稿の軽量サマリーを返す(body なし 本文は `get_post` を使用)。検索/ソート/フィルター対応。`{ posts, total, offset, limit }` を返す |
66
66
  | `get_post` | reader | slug または postId で単一の投稿を取得 |
67
67
  | `create_post` | editor | 新しい投稿を作成(下書きまたは公開済み) |
68
68
  | `update_post` | editor | 既存の投稿のフィールドをパッチ更新 |
package/README.md CHANGED
@@ -62,7 +62,7 @@ import type { ToolDefinition, ToolContext, ResolvedSite } from '@ampless/mcp-ser
62
62
 
63
63
  | Tool | Role | Description |
64
64
  |---|---|---|
65
- | `list_posts` | reader | List posts with optional status filter and pagination |
65
+ | `list_posts` | reader | Returns lightweight post summaries (no body — use `get_post` for content) with search / sort / filters. Returns `{ posts, total, offset, limit }` |
66
66
  | `get_post` | reader | Fetch a single post by slug or postId |
67
67
  | `create_post` | editor | Create a new post (draft or published) |
68
68
  | `update_post` | editor | Patch fields on an existing post |
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/tools/list-posts.ts
2
+ import { filterSortPostSummaries } from "ampless";
3
+
1
4
  // src/tools/post-mapping.ts
2
5
  import {
3
6
  decodeAwsJson
@@ -14,11 +17,28 @@ var POST_FIELDS = (
14
17
  body
15
18
  status
16
19
  publishedAt
20
+ updatedAt
17
21
  tags
18
22
  metadata
19
23
  }
20
24
  `
21
25
  );
26
+ var POST_SUMMARY_FIELDS = (
27
+ /* GraphQL */
28
+ `
29
+ fragment PostSummaryFields on Post {
30
+ postId
31
+ slug
32
+ title
33
+ excerpt
34
+ format
35
+ status
36
+ publishedAt
37
+ updatedAt
38
+ tags
39
+ }
40
+ `
41
+ );
22
42
  function toCorePost(p) {
23
43
  const metadata = decodeAwsJson(p.metadata);
24
44
  return {
@@ -30,20 +50,34 @@ function toCorePost(p) {
30
50
  body: decodeAwsJson(p.body),
31
51
  status: p.status ?? "draft",
32
52
  publishedAt: p.publishedAt ?? void 0,
53
+ updatedAt: p.updatedAt ?? void 0,
33
54
  tags: (p.tags ?? []).filter((t) => typeof t === "string"),
34
55
  metadata: metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata : void 0
35
56
  };
36
57
  }
58
+ function toPostSummary(p) {
59
+ return {
60
+ postId: p.postId,
61
+ slug: p.slug,
62
+ title: p.title,
63
+ excerpt: p.excerpt ?? void 0,
64
+ format: p.format ?? "markdown",
65
+ status: p.status ?? "draft",
66
+ publishedAt: p.publishedAt ?? void 0,
67
+ updatedAt: p.updatedAt ?? void 0,
68
+ tags: (p.tags ?? []).filter((t) => typeof t === "string")
69
+ };
70
+ }
37
71
 
38
72
  // src/tools/list-posts.ts
39
73
  var QUERY = (
40
74
  /* GraphQL */
41
75
  `
42
- ${POST_FIELDS}
76
+ ${POST_SUMMARY_FIELDS}
43
77
  query ListPosts($filter: ModelPostFilterInput, $limit: Int, $nextToken: String) {
44
78
  listPosts(filter: $filter, limit: $limit, nextToken: $nextToken) {
45
79
  items {
46
- ...PostFields
80
+ ...PostSummaryFields
47
81
  }
48
82
  nextToken
49
83
  }
@@ -53,29 +87,95 @@ var QUERY = (
53
87
  var listPostsSchema = {
54
88
  type: "object",
55
89
  properties: {
90
+ query: {
91
+ type: "string",
92
+ description: "Case-insensitive substring match over title / slug / tags"
93
+ },
94
+ tag: {
95
+ type: "string",
96
+ description: "Exact tag filter"
97
+ },
56
98
  status: {
57
99
  type: "string",
58
100
  enum: ["draft", "published", "all"],
59
101
  description: 'Filter by status (default "all")'
60
102
  },
61
- limit: { type: "integer", minimum: 1, maximum: 100, description: "Max results (default 20)" },
62
- nextToken: { type: "string", description: "Pagination cursor from a previous call" }
103
+ sort: {
104
+ type: "string",
105
+ enum: [
106
+ "updated-desc",
107
+ "updated-asc",
108
+ "published-desc",
109
+ "published-asc",
110
+ "title-asc",
111
+ "title-desc"
112
+ ],
113
+ description: 'Sort order (default "updated-desc" \u2014 most recently edited first)'
114
+ },
115
+ limit: {
116
+ type: "integer",
117
+ minimum: 1,
118
+ maximum: 100,
119
+ description: "Max results per page (default 20)"
120
+ },
121
+ offset: {
122
+ type: "integer",
123
+ minimum: 0,
124
+ description: "Zero-based offset into the filtered result set (default 0)"
125
+ }
63
126
  }
64
127
  };
128
+ var VALID_STATUS = /* @__PURE__ */ new Set(["draft", "published", "all"]);
129
+ var VALID_SORT = /* @__PURE__ */ new Set([
130
+ "updated-desc",
131
+ "updated-asc",
132
+ "published-desc",
133
+ "published-asc",
134
+ "title-asc",
135
+ "title-desc"
136
+ ]);
137
+ function asString(v) {
138
+ return typeof v === "string" ? v : void 0;
139
+ }
140
+ function asInt(v, fallback) {
141
+ const n = Number(v);
142
+ return Number.isFinite(n) ? Math.trunc(n) : fallback;
143
+ }
65
144
  async function listPosts(client, args = {}) {
66
- const status = args.status ?? "all";
145
+ const limit = Math.min(100, Math.max(1, asInt(args.limit, 20)));
146
+ const offset = Math.max(0, asInt(args.offset, 0));
147
+ const rawStatus = asString(args.status);
148
+ const status = rawStatus && VALID_STATUS.has(rawStatus) ? rawStatus : "all";
149
+ const rawSort = asString(args.sort);
150
+ const sort = rawSort && VALID_SORT.has(rawSort) ? rawSort : void 0;
151
+ const query = asString(args.query);
152
+ const tag = asString(args.tag);
67
153
  const filter = {};
68
154
  if (status !== "all") filter.status = { eq: status };
69
155
  const hasFilter = Object.keys(filter).length > 0;
70
- const data = await client.query(QUERY, {
71
- filter: hasFilter ? filter : void 0,
72
- limit: args.limit ?? 20,
73
- nextToken: args.nextToken
156
+ const allItems = [];
157
+ let cursor = null;
158
+ do {
159
+ const data = await client.query(QUERY, {
160
+ filter: hasFilter ? filter : void 0,
161
+ limit: 200,
162
+ nextToken: cursor
163
+ });
164
+ for (const item of data.listPosts.items) {
165
+ allItems.push(toPostSummary(item));
166
+ }
167
+ cursor = data.listPosts.nextToken;
168
+ } while (cursor);
169
+ const filtered = filterSortPostSummaries(allItems, {
170
+ query,
171
+ tag,
172
+ sort,
173
+ // status already applied server-side; pass 'all' to avoid double-filter
174
+ status: "all"
74
175
  });
75
- return {
76
- posts: data.listPosts.items.map(toCorePost),
77
- nextToken: data.listPosts.nextToken
78
- };
176
+ const total = filtered.length;
177
+ const posts = filtered.slice(offset, offset + limit);
178
+ return { posts, total, offset, limit };
79
179
  }
80
180
 
81
181
  // src/tools/get-post.ts
@@ -1593,7 +1693,7 @@ async function commitStaticPost(graphql, storage, args) {
1593
1693
  var tools = [
1594
1694
  {
1595
1695
  name: "list_posts",
1596
- description: "List posts in the CMS with optional filters by status. Returns up to `limit` posts (default 20) plus a `nextToken` cursor for pagination.",
1696
+ description: "Returns lightweight post summaries (no body \u2014 use get_post for content) with search / sort / filters. `total` reflects the filtered count. Supports query (substring over title/slug/tags), tag (exact), status, sort, limit (1\u2013100, default 20), offset (default 0).",
1597
1697
  inputSchema: listPostsSchema,
1598
1698
  handler: (args, ctx) => listPosts(ctx.graphql, args)
1599
1699
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ampless/mcp-server",
3
- "version": "1.0.0-alpha.54",
3
+ "version": "1.0.0-alpha.56",
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.48"
29
+ "ampless": "1.0.0-alpha.50"
30
30
  },
31
31
  "keywords": [
32
32
  "ampless",