Skip to main content
Glama

Fabrement

Save fields

fabrement-save-fields

Define the custom fields of a post type (fields, the form layout, or both; a section you leave out stays as it is). Editors then fill the values in the "Fabrement fields" box of the block editor (saved with Update); set-field-values sets them too. Site-wide settings are options pages: save-options-page. Read get-fields first and send its revision as expectedRevision. "fields" is the complete active list: start from get-fields, change it, send it all (without its "retired": true fields). Everything is checked before writing: on any error nothing is saved and the error starts with the path of the input to fix (e.g. "fields[2].sub_fields[0].name: ..."). Returns the stored fields. Rules: get-skill-doc {id:"fields-contract"} — read it before the first call.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
slugYesPost type slug, e.g. "project" or "page".
typeYesOwner kind: "post", any post type with an admin screen (page, post, a Fabrement CPT, a plugin's type). Site-wide settings are not fields of a type: they are options pages (get-options-page / save-options-page).
fieldsNoThe COMPLETE list of active fields, as a whole. A field you leave out is retired (hidden, values kept), never deleted. get-fields lists retired ones with "retired": true: leave them out (they stay retired), or send one without "retired" to restore it. "key" may be left out. A field IS its name: a new name is a new, EMPTY field (there is no rename: to rename what editors see, change the label; a real name change is a migration, see fields-contract RENAMING). Each field, sub-fields included: {name, label (required, non-empty, ≤200: what editors see), description (required, non-empty, ≤2000: the hint editors see under the field), type (text|textarea|wysiwyg|number|true_false|select|icon|date_picker|date_time_picker|image|gallery|file|link|post_object|relationship|group|repeater), translation? (translate|copy|copy_once|ignore; what a language version gets: translate = its own words, seeded from the original and sent to translators (text/textarea/wysiwyg/link only); copy = one value for every language, locked in translations; copy_once = seeded once, then each language has its own (a price, a date or a picked record that differs per language); ignore = never touched. A repeater under copy takes its ROW LIST from the original, each sub-field following its own policy; a group under copy is one set whose sub-fields each follow their own policy (clearing it on the original clears only its copy sub-fields in translations). Omitted: translate for text/textarea/wysiwyg/link, copy for everything else; an existing field keeps its policy. It only matters once the site has several languages), required?, default?, format? (number: integer|decimal), min?/max? (number bounds; repeater/relationship/gallery item counts: max enforced, min only a hint shown in the form), choices? (select: [{value,label}]; icon: [{value: a library icon id from list-icons, e.g. "social/facebook", label}] — the editor picks one or uploads an image instead), postTypes? (post_object/relationship: the post type slugs that may be picked, e.g. ["project"]; omitted = any public type; see list-post-types), sub_fields? (group/repeater)}. name: a-z, digits, single underscores, starts with a letter, not with "fab_", at most 24 characters, no part only digits (step_2 is refused, step2 is fine); unique among siblings; templates read it with fabrement_get_field('name', $id). The type and number format of a name never change (a retired field keeps its name too): add a new field instead. The same top-level name on another post type is the same meta key, so it must have the same type and number format there.
layoutNoForm layout only; never affects stored values. {tabs: [{label, items: [{field: <name or key of an active top-level field>, width?: 1/4|1/3|1/2|2/3|3/4|1, display?: block|row|table (repeater only)}]}]}. Fields not placed are shown after the tabs. Short numbers, dates and switches: 1/2 or 1/3; long text, wysiwyg and repeaters: 1. Omit layout for an automatic grid.
expectedRevisionYesrevision from get-fields; 0 for a post type without fields.

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observed

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the annotations by disclosing atomic all-or-nothing validation ('on any error nothing is saved'), optimistic concurrency via expectedRevision, the deterministic error format ('fields[2].sub_fields[0].name: ...'), and partial-update semantics ('a section you leave out stays as it is'). With readOnlyHint=false and destructiveHint=false already set, this adds substantial non-obvious behavioral context about write safety.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Long and dense, but the purpose is front-loaded in the first clause and the remaining sentences carry contract rules that an agent genuinely needs. The trailing 'Rules:' line is slightly buried and some nesting could be flattened, but there is little pure filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a nested, 5-parameter mutation tool with no output schema, the description covers the missing pieces: return value ('Returns the stored fields'), concurrency contract, failure behavior, and partial-update semantics. An agent has everything needed to invoke this correctly on the first call.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds procedural meaning the schema does not carry: that 'fields' is a complete replacement list, how retired fields should be omitted, and that expectedRevision comes from get-fields (0 for an unconfigured type). It stops short of syntax detail for layout beyond pointing at the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and resource: 'Define the custom fields of a post type (fields, the form layout, or both)'. It explicitly distinguishes itself from siblings — set-field-values ('sets them too') and save-options-page for site-wide settings — so an agent can route correctly without opening other schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

States prerequisites explicitly ('Read get-fields first and send its revision as expectedRevision', 'Read get-skill-doc {id:"fields-contract"} before the first call') and names the alternatives for adjacent cases (set-field-values, save-options-page, get-fields). Nothing about when to reach for this tool is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources