@latellu/atlas-agent-skills 0.5.3 → 0.6.0

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,6 @@
1
1
  {
2
2
  "name": "atlas",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "description": "Work with Atlas CMS from Claude Code: bundles the Atlas MCP server (read/write content via tools) and the atlas-cms skill that teaches agents when and how to use the MCP tools, the @latellu/atlas-sdk delivery/management clients, and the @latellu/atlas-cli type generator.",
5
5
  "author": {
6
6
  "name": "Latellu",
@@ -9,5 +9,11 @@
9
9
  "homepage": "https://docs.atlas.latellu.com/docs/agent-skill",
10
10
  "repository": "https://github.com/tenriajeng/atlas-agent-skills",
11
11
  "license": "MIT",
12
- "keywords": ["atlas", "cms", "headless-cms", "mcp", "sdk"]
12
+ "keywords": [
13
+ "atlas",
14
+ "cms",
15
+ "headless-cms",
16
+ "mcp",
17
+ "sdk"
18
+ ]
13
19
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@latellu/atlas-agent-skills",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "description": "Agent skill + MCP configuration for Atlas CMS. Works as a Claude Code plugin and as plain markdown guidance for any AI coding agent (Cursor, Codex, opencode, Gemini CLI, ...).",
5
5
  "license": "MIT",
6
6
  "author": "Latellu (https://github.com/tenriajeng)",
@@ -9,7 +9,15 @@
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/tenriajeng/atlas-agent-skills.git"
11
11
  },
12
- "keywords": ["atlas", "cms", "headless-cms", "mcp", "agent", "skill", "claude-code"],
12
+ "keywords": [
13
+ "atlas",
14
+ "cms",
15
+ "headless-cms",
16
+ "mcp",
17
+ "agent",
18
+ "skill",
19
+ "claude-code"
20
+ ],
13
21
  "files": [
14
22
  ".claude-plugin",
15
23
  ".mcp.json",
@@ -43,8 +43,10 @@ Two key classes, both sent as the `X-API-Key` header (never `Authorization: Bear
43
43
  drafts.
44
44
  - Management keys act as a service actor bound to the RBAC permissions of whoever
45
45
  created them, further limited by scopes: `content:write` (create/update/delete),
46
- `content:publish` (publish/unpublish/archive/schedule), `media:write` (uploads).
47
- A missing scope → 403 on that operation only.
46
+ `content:publish` (publish/unpublish/archive/schedule), `media:write` (uploads),
47
+ `schema:write` (content-type/field authoring). A missing scope → 403 on that operation
48
+ only. Scopes don't imply one another, and keys minted before a scope existed don't
49
+ gain it — if schema tools 403, the key needs `schema:write` added in the dashboard.
48
50
 
49
51
  **Safety rule:** never ship an `atlas_mgmt_*` key or the management client in
50
52
  browser-delivered code. Management usage is server-side only (API routes, scripts, CI).
@@ -92,13 +94,23 @@ means a few manual edits typed out in conversation, not a bulk script.
92
94
 
93
95
  ## Hard capability boundaries (don't go looking for these)
94
96
 
95
- - **Schema is dashboard-only.** No API, SDK, CLI, or MCP surface creates content types,
96
- fields, or block types. "Create a content type" = ask the user to do it in the
97
- dashboard, then continue.
97
+ - **Schema authoring stops at the shape of stored data.** With a `schema:write` key you
98
+ CAN create content types, add fields, edit a field's label/filterable/sortable, reorder
99
+ fields, and delete a content type that still has zero entries. You CANNOT rename a
100
+ field, change its `field_type`, toggle `localizable`/`is_unique`/`required`, edit
101
+ `validation`, delete a field, or toggle `is_block` — those rewrite or invalidate data
102
+ already in entries and are dashboard-only. Don't hunt for a workaround (deleting and
103
+ re-adding a field is NOT one — it drops the data): call `plan_field_change`, then give
104
+ the user its numbers and dashboard link. Available in MCP only; the SDK has no
105
+ schema methods.
98
106
  - **No field-value queries.** The public list endpoints filter by `type`/`locale` only —
99
107
  "all articles where category = X" means paging through and filtering client-side.
100
- - **The management plane is write-only** (no GET endpoints). Reading always needs a
101
- live key; a write-then-verify flow therefore needs both keys.
108
+ - **The management plane has no entry/page/media reads.** Its only GETs are the
109
+ content-type/field ones (`/manage/content-types...`, which exist because the public
110
+ schema omits field ids). Reading content still needs a live key, so a
111
+ write-then-verify flow needs both keys.
112
+ - **A live key never sees drafts** and there is no `status` parameter to change that —
113
+ visibility follows the key's environment (`production` vs `preview`).
102
114
 
103
115
  Deep API detail lives at https://docs.atlas.latellu.com — link there rather than
104
116
  duplicating endpoint documentation into this skill.
@@ -109,7 +109,9 @@ The real write shape (`POST/PUT /pages`) differs from what the reading API shows
109
109
  `get_workspace_schema` tool) returns each content type's `id`, `is_block`, and field
110
110
  list. Use the `id` of an `is_block: true` type as `block_type_id`, and compose the
111
111
  block's `data` against its fields exactly like an entry. (Older backends omit
112
- `id`/`is_block` from the schema — there, block-type UUIDs are dashboard-only.)
112
+ `id`/`is_block` from the schema — there, block-type UUIDs are dashboard-only.) A new
113
+ block type can be created with MCP `create_content_type` (`is_block: true`) given a
114
+ `schema:write` key; only flipping `is_block` on an *existing* type is dashboard-only.
113
115
  - MCP `create_page`/`update_page` caveats (≥ 1.1.0): `title` is an alias mapped to
114
116
  `seo.title` (on update, `seo` is replaced wholesale — title alone drops the other seo
115
117
  fields), and `seo_translations`/per-block `translations` are regular arguments.
@@ -1,8 +1,9 @@
1
1
  # Atlas MCP tools
2
2
 
3
3
  The `@latellu/atlas-mcp` server (stdio) exposes Atlas content operations as agent tools.
4
- This file describes `@latellu/atlas-mcp` **≥ 1.2.0** (the bundled `.mcp.json` uses
5
- `npx -y`, so it always runs the latest). Older servers lack `translations`,
4
+ This file describes `@latellu/atlas-mcp` **≥ 1.5.0** (the bundled `.mcp.json` uses
5
+ `npx -y`, so it always runs the latest). Servers below 1.5.0 lack the schema-authoring
6
+ tools and the `locale` parameter on reads; below 1.2.0 they also lack `translations`,
6
7
  `seo_translations`, per-field error detail, the schema `id`/`is_block` fields, page/entry lifecycle tools (unpublish/archive/duplicate/schedule), bulk operations, reorder tools, media alt-text editing, list meta+cursor pagination, automatic 429 retry, and pre-parsed block data in get_page.
7
8
 
8
9
  **Environment** — read tools need `ATLAS_LIVE_API_KEY`, write tools need
@@ -23,14 +24,27 @@ errors).
23
24
  |---|---|---|
24
25
  | `get_workspace_schema` | — | `{ workspace: { slug, name, locales, default_locale }, content_types: [...] }` — each content type carries `id` and `is_block`; types with `is_block: true` are the valid `block_type_id` values for page blocks |
25
26
  | `list_content_types` | — | `content_types` array (client-side view of the schema) |
26
- | `get_content_type` | `content_type` | one content type with its full field list; error text if the slug doesn't exist |
27
+ | `get_content_type` | `content_type` | one content type with its full field list; error text if the slug doesn't exist. **No field ids** — use `list_fields` for those |
28
+ | `list_fields` | `content_type` | field definitions **with `id`** (mgmt key). The ids are what `update_field` and `reorder_fields` address |
29
+
30
+ ### Schema authoring — mgmt key with `schema:write`
31
+
32
+ | Tool | Params | Notes |
33
+ |---|---|---|
34
+ | `create_content_type` | `name`, `slug?`, `is_block?`, `orderable?` | slug auto-generated from name when omitted; `is_block: true` makes it usable as a page block |
35
+ | `update_content_type` | `content_type`, `name?`, `description?`, `icon?`, `display_field?`, `orderable?` | metadata only; slug is immutable and `is_block` is not accepted |
36
+ | `delete_content_type` | `content_type` | **409 while any entry exists** — so it only undoes an empty type you just created |
37
+ | `add_field` | `content_type`, `name`, `label`, `field_type`, `required?`, `is_unique?`, `localizable?`, `filterable?`, `sortable?`, `options?`, `validation?` | `options` is a plain array (encoded for you). Pick `field_type` carefully — it cannot be changed later via API |
38
+ | `update_field` | `content_type`, `field_name` \| `field_id`, `label?`, `filterable?`, `sortable?` | presentation only; `field_name` is resolved to an id for you |
39
+ | `reorder_fields` | `content_type`, `order` | complete ordered list of field **ids** (from `list_fields`), not a patch |
40
+ | `plan_field_change` | `content_type`, `field_name` \| `field_id` | read-only impact report + `dashboard_url`; use for any change the API refuses |
27
41
 
28
42
  ### Entries — read: live key · write: mgmt key
29
43
 
30
44
  | Tool | Params | Notes |
31
45
  |---|---|---|
32
- | `list_entries` | `content_type`, `page?`=1, `limit?`=20, `status?`, `cursor?` | `status` is enum (draft \| published \| archived \| scheduled); live key **cannot** reveal drafts. Returns `{ data, meta }` where `meta` has `total_data`, `total_pages` (offset mode) or `next_cursor` (cursor mode). Use `cursor: meta.next_cursor` to page past the 1000-page limit. |
33
- | `get_entry` | `content_type`, `slug` | resolves by `slug` alone — `content_type` is accepted but errors clearly if the slug belongs to a different content type |
46
+ | `list_entries` | `content_type`, `page?`=1, `limit?`=20, `cursor?`, `fields?`, `locale?`, `sort?`, `search?` | **No `status` param** visibility follows the key's environment (`production` = published only, `preview` = also draft/scheduled). Returns `{ data, meta }` where `meta` has `total_data`, `total_pages` (offset mode) or `next_cursor` (cursor mode). Use `cursor: meta.next_cursor` to page past the 1000-page limit. |
47
+ | `get_entry` | `content_type`, `slug`, `fields?`, `locale?` | resolves by `slug` alone — `content_type` is accepted but errors clearly if the slug belongs to a different content type |
34
48
  | `create_entry` | `content_type`, `data`, `slug?`, `translations?` | creates a **draft**; slug auto-generated when omitted; `translations` = `{locale: {data: {...}}}` (required+localizable fields go HERE) |
35
49
  | `update_entry` | `content_type`, `slug`, `data`, `translations?` | **full replace** of `data` (and per-locale `translations`), not a patch |
36
50
  | `publish_entry` / `unpublish_entry` / `archive_entry` | `content_type`, `slug` | need the `content:publish` scope |
@@ -90,17 +104,32 @@ The MCP manage plane is now fully covered. These gaps remain:
90
104
  must read-modify-write yourself: `get_entry` → merge your changes → `update_entry`.
91
105
  (The management SDK's `update` is a full replace too — this is API semantics, not an
92
106
  MCP quirk.)
93
- - **Schema is dashboard-only.** Creating content types, fields, or block types is not
94
- exposed via MCP, SDK, or API. "Create a content type" = ask the user to do it in the
95
- dashboard, then continue.
107
+ - **Schema changes that touch stored data are dashboard-only.** Creating types and fields
108
+ IS available (see "Schema authoring" above, needs `schema:write`). What is not: renaming
109
+ a field, changing its `field_type`, toggling `localizable`/`is_unique`/`required`,
110
+ editing `validation`, deleting a field, deleting a type that has entries, toggling
111
+ `is_block`. For those, call `plan_field_change` and hand the user its affected-entry
112
+ counts plus `dashboard_url`. **Delete-and-re-add is not a workaround** — it discards the
113
+ data the rename was meant to preserve.
114
+ - **Schema authoring is MCP-only.** `@latellu/atlas-sdk`'s management client has no
115
+ content-type methods; don't look for them there.
96
116
 
97
117
  Before composing any `data` payload, read `authoring.md` — it defines the exact value
98
118
  format per field type (richtext = Tiptap HTML, relation/image = UUIDs, etc.).
99
119
 
100
120
  ## Gotchas
101
121
 
102
- - **Drafts are invisible to live keys** — `list_entries(status="draft")` returning
103
- empty means key scoping, not an empty workspace.
122
+ - **Drafts are invisible to live keys** — an empty `list_entries` means key scoping, not
123
+ an empty workspace. There is no `status` parameter to override it; you need a `preview`
124
+ environment key.
125
+ - **Reads are base-locale unless you pass `locale`** — write a translation, then
126
+ `get_entry` without `locale`, and it looks like the write silently failed. Pass the
127
+ locale to verify your own writes.
128
+ - **Field ids are not in the schema** — `get_workspace_schema` / `get_content_type` omit
129
+ them by design. `update_field` accepts `field_name` so you rarely need them, but
130
+ `reorder_fields` needs real ids from `list_fields`.
131
+ - **A freshly created content type appears immediately**, but only because schema writes
132
+ bust the delivery response cache server-side; don't add sleeps or retries around it.
104
133
  - **`get_entry` looks up by slug alone** (slug-global, `content_type` is not sent to the
105
134
  API) — but if the entry found belongs to a different content type than you requested,
106
135
  the tool returns a clear mismatch error instead of the wrong entry.
@@ -68,7 +68,10 @@ Construction rules:
68
68
  - Requests carry `X-API-Key` and go to `${url}/api/v1/manage/*`.
69
69
  - The key's scopes gate operations: `content:write` → create/update/delete/duplicate,
70
70
  `content:publish` → publish/unpublish/archive/schedule, `media:write` → uploads.
71
- Missing scope = 403 on that call only. Every write is audit-attributed to the key's
71
+ Missing scope = 403 on that call only.
72
+ - **No schema methods.** Content-type and field authoring (the `schema:write` scope) is
73
+ exposed through the MCP server only — this SDK has no equivalent. Reach for MCP, or call
74
+ `/api/v1/manage/content-types` directly, when you need to build schema from code. Every write is audit-attributed to the key's
72
75
  creator and bounded by their RBAC — a key can never do more than its creator could in
73
76
  the dashboard.
74
77