@mapled/mcp 0.12.0 → 0.13.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.
package/README.md CHANGED
@@ -48,9 +48,11 @@ For the first integration, propose one plan and let the owner approve it on a tr
48
48
  5. Wire the site: `get_connection`, `configure_revalidation`, deploy, then close with `report_setup` (files changed, `buildPassed`, `secretsCommitted: false`). Mapled runs its own checks — the site reads content, the webhook delivered, the preview route responds, fields have help texts — and the run is completed only when they pass. Fix what failed and call `verify_setup`.
49
49
  6. Leave a guide: `get_mapled_md` renders `MAPLED.md` from the project — what the site reads and where, the content model, the working rules, the commands that verify the integration. Write it to the repository root and commit it; the next agent (or person) starts from it. When the file exists, replace everything above its `<!-- mapled:notes -->` line and keep the notes below.
50
50
 
51
- ## Destructive changes
51
+ ## Destructive and breaking changes
52
52
 
53
- `delete_collection`, `remove_field` and `clear_records` never act on their own. Each one files a request and returns a `reviewUrl`: send it to the user, who confirms on a trusted Mapled screen by typing the name. Poll `get_confirmation` until the status is `applied` (the result says what changed), `denied` or `expired` (after an hour). Nothing is deleted until then.
53
+ `delete_collection`, `remove_field` and `clear_records` never act on their own, and neither do the two breaking schema changes — `rename_field` (a field's key, what the site's code reads) and `convert_field` (its type, converting the stored values). Each one files a request and returns a `reviewUrl`: send it to the user, who confirms on a trusted Mapled screen by typing the name. Poll `get_confirmation` until the status is `applied` (the result says what changed), `denied` or `expired` (after an hour). Nothing changes until then.
54
+
55
+ A rename or a conversion Mapled wouldn't take is refused at once with the reason: a key that is taken or malformed, a type the field can't become, a formula that reads the field, values that can't be converted (with their count and examples — fix the content, or ask again with `clearFailed: true`; the person confirming sees how many will be cleared). After a rename the old key keeps working as an alias until a person removes it, so move the site's code to the new key first. Both can be undone from Field settings.
54
56
 
55
57
  ## Tools
56
58
 
package/dist/tools.js CHANGED
@@ -41,9 +41,9 @@ export const FIELD_TYPES = [
41
41
  ];
42
42
  /** What add_field and propose_setup_plan say about formulas (§14.9). */
43
43
  const COMPUTED_HELP = "For type computed only: { expression } — a formula over the record's other fields that Mapled evaluates when a record is read or published (read-only for editors and agents; the site reads the value like any field of the result type, filters and sorts included; writes ignore it). " +
44
- "Field keys as written (price, unit-cost — put spaces around a minus to subtract: price - cost); one link deep through relations: author.name, and lists over many-relations for aggregates: sum(items.price), count(tags), join(tags.name, \", \"). " +
44
+ "Field keys as written (price, unit-cost — put spaces around a minus to subtract: price - cost); one link deep through relations: author.name, and lists over many-relations for aggregates: sum(items.price), count(tags), join(tags.name, \", \"); or one link back — the records of another collection whose relation points at this record, written @collection.relation[.field], always a list, oldest first: count(@posts.author), sum(@order-items.order.total), max(@posts.author.published-on). " +
45
45
  "Numbers: + - * / %, round(x, digits), floor, ceil, abs, min, max, sum, avg, count, fixed(x, digits) → text. Text: & joins (empty counts as \"\"), concat, upper, lower, trim, length, left(s, n), right(s, n), replace(s, from, to), contains(s, part), slug(s), text(x), number(s). " +
46
- "Logic: = != < <= > >=, and, or, not, if(cond, a, b), coalesce(a, b), empty(x). Dates: year, month, day, date(datetime), daysBetween(a, b), addDays(d, n). Literals: 12, 2.5, \"text\", true, false, null. " +
46
+ "Logic: = != < <= > >=, and, or, not, if(cond, a, b), coalesce(a, b), empty(x). Dates: year, month, day, date(datetime), daysBetween(a, b), addDays(d, n), created() — when the record was added (a datetime). Literals: 12, 2.5, \"text\", true, false, null. " +
47
47
  "Sensitive fields, groups, JSON and location can't be read; there is no now(). The result type (number, text, boolean, date, datetime) follows from the formula.";
48
48
  export function createTools(api) {
49
49
  return [
@@ -215,6 +215,52 @@ export function createTools(api) {
215
215
  fieldKey: args.fieldKey,
216
216
  }),
217
217
  },
218
+ {
219
+ name: "rename_field",
220
+ description: "Breaking change, a person's decision: rename a field's key (what the site's code reads). The values follow, formulas and the " +
221
+ "schema are updated, and the old key keeps working as an alias — delivery serves the value under both keys, and get_schema lists " +
222
+ "it under `aliases` — until a person removes it in Field settings, so the site never breaks mid-change. Nothing changes until a " +
223
+ "person confirms it on a trusted Mapled screen by typing the field's name (returns a reviewUrl to send to the user); poll " +
224
+ "get_confirmation. After it is applied: update the code to read the new key, regenerate the types, push the manifest. A refusal " +
225
+ "(key taken, malformed key) comes back at once. It can be undone from Field settings.",
226
+ schema: {
227
+ collectionKey: z.string().min(1).max(120),
228
+ fieldKey: z.string().min(1).max(120).describe("The field's current key."),
229
+ newKey: z.string().min(1).max(50).describe("Lowercase letters, digits and hyphens, up to 50 characters."),
230
+ },
231
+ handler: async (args) => api.request("POST", "/v1/agent/confirmations", {
232
+ action: "rename_field",
233
+ collectionKey: args.collectionKey,
234
+ fieldKey: args.fieldKey,
235
+ newKey: args.newKey,
236
+ }),
237
+ },
238
+ {
239
+ name: "convert_field",
240
+ description: "Breaking change, a person's decision: change a field's type, converting the stored values (get_schema lists the types a field " +
241
+ "can become under `convertibleTo`). Mapled works the conversion out over every record first: if some values can't be converted " +
242
+ "the request is refused with their count and a few examples — fix the content, or ask again with clearFailed: true to clear " +
243
+ "those values (the person confirming sees how many). A conversion that would break a formula reading the field is refused too. " +
244
+ "Nothing changes until a person confirms it on a trusted Mapled screen by typing the field's name (returns a reviewUrl to send " +
245
+ "to the user); poll get_confirmation. It can be undone from Field settings, with the previous values.",
246
+ schema: {
247
+ collectionKey: z.string().min(1).max(120),
248
+ fieldKey: z.string().min(1).max(120),
249
+ type: z.string().min(1).max(40).describe("The type to convert to — one of the field's convertibleTo."),
250
+ cardinality: z.enum(["one", "many"]).optional().describe("For a relation: one ↔ many."),
251
+ options: z.array(z.string().min(1).max(60)).max(60).optional().describe("For enum: the choices; derived from the values when omitted."),
252
+ clearFailed: z.boolean().optional().describe("Clear the values that can't be converted instead of refusing."),
253
+ },
254
+ handler: async (args) => api.request("POST", "/v1/agent/confirmations", {
255
+ action: "convert_field",
256
+ collectionKey: args.collectionKey,
257
+ fieldKey: args.fieldKey,
258
+ type: args.type,
259
+ ...(args.cardinality !== undefined ? { cardinality: args.cardinality } : {}),
260
+ ...(args.options !== undefined ? { options: args.options } : {}),
261
+ ...(args.clearFailed !== undefined ? { clearFailed: args.clearFailed } : {}),
262
+ }),
263
+ },
218
264
  {
219
265
  name: "clear_records",
220
266
  description: "Destructive: move every record of a collection to Trash (kept 7 to 90 days by the project's plan), e.g. before re-importing content. " +
@@ -228,8 +274,8 @@ export function createTools(api) {
228
274
  },
229
275
  {
230
276
  name: "get_confirmation",
231
- description: "Check a destructive-change request: status is pending (waiting for the person), applied (done — result " +
232
- "says what changed), denied, expired (after an hour; request again if still needed) or failed.",
277
+ description: "Check a request that waits for a person (a destructive change, a rename, a conversion): status is pending (waiting for the " +
278
+ "person), applied (done — result says what changed), denied, expired (after an hour; request again if still needed) or failed.",
233
279
  schema: { confirmationId: z.string().uuid() },
234
280
  handler: async (args) => api.request("GET", `/v1/agent/confirmations/${encodeURIComponent(args.confirmationId)}`),
235
281
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mapled/mcp",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Mapled MCP server: lets AI agents build schema and content for one Mapled project.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://mapled.io",