Guide for creating Kuroco Content Structures (TopicsGroup) using an MCP tool.
Call /kuroco-content-structure-design first. This skill only covers the syntax and tool usage for creating a fixed design with Admin MCP. Design decisions such as how to split TopicsGroups, how to represent master data (CSV table vs relation), how to use categories/tags/relations, andext_slugnaming policies should first be finalized with /kuroco-content-structure-design if not already done.
Source of Truth: Build each field with the property vocabulary documented in this skill (options, required, and the per-type properties below) — these are what the write handler consumes. Use the tool's live inputSchema for the top-level parameters (see Content Definition Parameters); that object sets additionalProperties: false, so an undeclared top-level key is rejected.
Verify by read-back after every write (see Verifying the result). A successful response means the call was accepted, not that every field landed as intended — check before reporting success to the user.
Table of Contents
- Finding the MCP Tool
- Basic Structure
- Content Definition Parameters — body field, publish scope, viewing/editing limits
- Verifying the result — mandatory read-back after every write
- Common Field Properties
- Field Type Reference
- Field Type Details
- Field Groups (Repeatable)
- Practical Examples
Finding the MCP Tool
Content Structures are managed through the Kuroco Admin MCP server (/direct/rcms_api/admin_mcp/). Group-definition CRUD requires the topics_group scope in the connection URL (e.g., /x/topics_group or /x/all). See /kuroco-admin-mcp for connection setup, OAuth authentication, and scopes.
How to find the correct tool:
- Verify the connection with the
whoamitool (present on every scoped endpoint, includingreadonlyones), then runtools/list.- Group definitions live under the
topicspermission module, somcp:tools.writeis enough to create them. - But that level cannot create API endpoints (
rcms_apiis outside its ceiling). If the user's goal includes exposing this content through an API, tell them up front that the endpoint step needsmcp:tools.allor higher — otherwise they get a content definition they cannot serve. See/kuroco-admin-mcp. - Check
permissions.approval_requiredtoo: iftopicsis listed, writes enter the approval workflow instead of taking effect directly.
- Group definitions live under the
- Look for the group-definition tools — with the standard naming convention this is
topics_group-create(list/update/delete follow the sametopics_group-{verb}pattern). Tool names are generated dynamically, so always confirm the exact name andinputSchemaviatools/list— never guess. - If no matching tool appears, check the connection scope (
topics_groupmissing, or areadonlyURL hides write tools — see the/kuroco-admin-mcpchecklist), or ask the user which MCP server they have configured. - Build your JSON against the tool's
inputSchemaand call the tool. Creating a content structure is a write operation — confirm with the user before executing.
A Content Structure (TopicsGroup) defines the data schema for content items (Topics), including custom fields (extension columns).
Recommended workflow
- Check the tool's live
inputSchema(tools/list) for the top-level parameters — that object rejects undeclared keys, so this is where the schema is authoritative. - Decide the container parameters from the request: body field (
content_input_type) and access rules (non_public_flg/secure_level/writer_groups). See Content Definition Parameters. Resolve any group IDs withgroup-list— never guess them. - Build the
fieldsarray using the property vocabulary in this document (options,required, per-type properties) — not the rawext_option/ext_limit_itemcolumns, which are silently ignored on write. - Confirm with the user, then send the JSON to the tool.
- Read the record back with
topics_group-getand verify the stored values. See Verifying the result.
Basic Structure
Top-level JSON structure:
{
"name": "Content structure name (required)",
"description": "Description of the content structure (optional)",
"fields": [
// Array of field definitions or field groups (at least one required)
]
}
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Name of the content structure |
description |
string | No | Description of the content structure |
fields |
array | Yes | Array of field definitions or field groups (minimum 1) |
Content Definition Parameters
name / description / fields are not the whole contract. The parameters below live at the top level of the same JSON and control the body field and the access rules for every record in this definition. They cannot be expressed as fields, and omitting them silently accepts defaults that are often wrong for the request.
| Parameter | Type | Description |
|---|---|---|
content_input_type |
number | 0=WYSIWYG body, 1=Textarea body, 2=Custom. 2 is the recommended value and is the default on API/MCP INSERT — leave it as is. 2 means the built-in contents field is not used and every item is built as an extension field in fields. When the content needs a main body, declare a WYSIWYG field (ext_type: 6) for it instead of switching to 0/1 |
open_flg |
number | 0=unpublished/disabled, 1=published/enabled (default 1). The UI label depends on non_public_flg |
non_public_flg |
number | 0=publish-oriented (default), 1=internal use only. secure_level is required when this is 1 |
secure_level |
integer[] | Viewing limit: group IDs allowed to view records. An empty array means publicly viewable |
writer_groups |
integer[] | Editing limit: group IDs allowed to insert/update/delete records |
Reading back the unset state: when you omit
secure_level/writer_groups/need_application_groups/my_topics_only_limit_groups/can_save_draft_groups, they are stored as the string"0", not as an empty string or empty array."0"means "no restriction configured" — do not read it as "group 0 is allowed". |need_application_groups| integer[] | Group IDs whose updates require approval (data_waiting) before going live | |my_topics_only_limit_groups| integer[] | Group IDs restricted to records they authored themselves | |can_save_draft_groups| integer[] | Group IDs allowed to save drafts instead of publishing immediately |
Mapping natural-language requests to these parameters — a request phrased in terms of visibility is a request for these parameters, not for a field:
| The user says | Set |
|---|---|
| "not disclosed externally," "for internal use," "internal only" | non_public_flg: 1 + secure_level: [<group ids>] |
| "visible only to the person in charge," "members only" | secure_level: [<group ids>] |
| "only the editorial department can edit" | writer_groups: [<group ids>] |
| "go through approval before publishing" | need_application_groups: [<group ids>] |
| "the article body is required," "blog / news article" | Keep content_input_type: 2 and declare a WYSIWYG field (ext_type: 6) in fields |
Do not switch to content_input_type: 0/1 just because the content has a body. 2 (Custom) is the recommended shape: every piece of content is an explicit, named field. The Practical Examples below follow this pattern — Example 1 declares its own Article Body WYSIWYG field while leaving content_input_type at its default.
Never guess group IDs. Resolve them with group-list first — group names and IDs differ per site (e.g. a site may have Administrator=1, Editor=2, User=104 with no contiguous numbering).
Verifying the result
A successful topics_group-create response does not prove your fields were stored as intended: the ignored properties above fail silently. After every create/update, read the record back and check the stored values.
// tool: topics_group-get
{ "topics_group_id": <returned id> }
Check formData in the response — it exposes the per-field stored state with a numeric suffix per field:
| What you intended | Field to check | Expected when it worked |
|---|---|---|
select / checkbox choices |
options_<n> |
{"news":"お知らせ",...} — the key→label map. (ext_option_<n> holds the same data serialized as "key::val" lines, "key::default::val" when an option carries "default": true) |
required: true |
ext_limit_item_<n> / limits_<n> |
"required=1" / {"required":"1"} |
| Field type | ext_type_<n> |
The ext_type you sent |
| Repeatable group | ext_group_loop_<n>, ext_group_parent_ext_col_<n> |
Repetition count on every member field; children point at the group's first field (e.g. "ext_3") |
| Access rules | non_public_flg, secure_level, writer_groups |
1, "1,2", "1,2" (stored as comma-separated strings) |
topics-describe is the complementary check: it reports the resulting API-facing shape of each field (e.g. a 5-repetition group appears as "type": "array", "maxItems": 5).
Report any discrepancy to the user rather than assuming the write took effect.
Common Field Properties
Properties shared by all field types:
| Property | Type | Required | Description |
|---|---|---|---|
ext_title |
string | Yes | Display name of the field |
ext_type |
number | Yes | Field type identifier (see reference table below) |
type |
string | No | Field type name (see reference table below). Not required — creation succeeds without it, since ext_type carries the type. Kept in the examples below because it is harmless and self-documenting |
ext_slug |
string | No | Slug for API access. Set this on every field. With a slug, the front API returns the field under that name (main_image). Without one, the key is positional, and its form differs per site: either ext_1 (plain counter) or ext_col_01 (zero-padded). whoami reports which one this site uses in site.topics_ext_key_format, and topics-describe is authoritative for a specific group. Positional keys also break when fields are reordered, so a slug removes both problems |
ext_help_msg |
string | No | Help text displayed to editors |
repetitions |
number | No | Number of repetitions allowed |
required |
boolean | No | Whether the field is required |
searchable |
boolean | No | Whether the field is searchable |
template |
string | No | Custom template HTML |
Field Type Reference
Note: This table is a static snapshot. Always verify
ext_type/typepairs against the tool's liveinputSchema(tools/list) — values may have changed.
| ext_type | type | Description |
|---|---|---|
| 0 | text |
Single-line text input |
| 1 | textarea |
Multi-line text area |
| 2 | select |
Select box (single choice) |
| 4 | image |
Image upload |
| 5 | checkbox |
Checkbox (multiple choice) |
| 6 | wysiwyg |
WYSIWYG rich text editor |
| 7 | link |
URL link input |
| 8 | date |
Date/datetime picker |
| 9 | file |
File upload |
| 10 | table |
Table input |
| 11 | location |
Geolocation (map) |
| 13 | textauto |
Autocomplete text |
| 20 | relation |
Relation (reference to other records) |
| 21 | html |
HTML code input |
| 27 | s3file |
Amazon S3 file upload |
| 28 | json |
JSON data input |
| 29 | csvtable |
CSV table |
| 30 | filemanager |
File manager |
| 31 | vimeo |
Vimeo video upload |
| 32 | api |
External API integration |
| 33 | gcsfile |
Google Cloud Storage file upload |
| 34 | counter |
Auto-increment counter |
| 35 | number |
Numeric input |
| 36 | bool |
Boolean (ON/OFF toggle) |
| 37 | csvtable_checkbox |
CSV table checkbox |
| 38 | block_editor |
Block-based content editor |
Field Type Details
Note: The details below are a static snapshot for quick reference. If any value conflicts with the tool's live
inputSchema, theinputSchematakes precedence. Check it first to confirm accepted values.
Text (ext_type: 0, type: "text")
Single-line text input field.
{
"ext_title": "Title",
"ext_type": 0,
"type": "text",
"required": true,
"searchable": true,
"placeholder": "Enter text...",
"default_value": "",
"min_length": 0,
"max_length": 255,
"type_limitation": "email"
}
| Parameter | Type | Description |
|---|---|---|
placeholder |
string | Placeholder text |
default_value |
string | Default value |
min_length |
number | Minimum character count |
max_length |
number | Maximum character count |
type_limitation |
string | Input validation type: "email", "tel", "zip", "url", "number", "regex" |
regex |
string | Regex pattern (when type_limitation is "regex") |
Textarea (ext_type: 1, type: "textarea")
Multi-line text input area.
{
"ext_title": "Description",
"ext_type": 1,
"type": "textarea",
"placeholder": "Enter description...",
"width": "100%",
"height": "200px",
"min_length": 0,
"max_length": 10000
}
| Parameter | Type | Description |
|---|---|---|
width |
string | Input width (e.g., "100%", "500px") |
height |
string | Input height (e.g., "200px", "10em") |
placeholder |
string | Placeholder text |
default_value |
string | Default value |
min_length |
number | Minimum character count |
max_length |
number | Maximum character count |
WYSIWYG Editor (ext_type: 6, type: "wysiwyg")
Rich text editor.
{
"ext_title": "Article Body",
"ext_type": 6,
"type": "wysiwyg",
"required": true,
"searchable": true,
"width": "100%",
"height": "400px",
"output_format": "html"
}
| Parameter | Type | Description |
|---|---|---|
width |
string | Editor width |
height |
string | Editor height |
toolbar |
boolean | Use simplified toolbar |
use_font_size_px |
boolean | Specify font sizes in pixels |
use_magicline |
boolean | Enable magic line feature |
use_markdown |
boolean | Enable Markdown input mode |
output_format |
string | Output format: "html" or "markdown" |
custom_css |
string | Custom CSS applied inside the editor |
resource |
string | Resource path for file uploads |
auto_use_iframely |
boolean | Use Iframely auto-embedding |
auto_use_token |
boolean | Auto-use authentication token |
default_value |
string | Default HTML value |
customColors |
string | Custom color settings |
largeColorPalette |
boolean | Use large color palette |
removePlugins |
string | Plugins to disable |
placeholders |
string | Placeholder settings |
Select Box (ext_type: 2, type: "select")
Single-choice field (dropdown or radio buttons). options is required.
{
"ext_title": "Category",
"ext_type": 2,
"type": "select",
"required": true,
"options": [
{ "key": "news", "val": "News", "default": true },
{ "key": "blog", "val": "Blog" },
{ "key": "event", "val": "Event" }
],
"use_radio": false
}
| Parameter | Type | Required | Description |
|---|---|---|---|
options |
array | Yes | List of choices (minimum 1) |
options[].key |
string | Yes | Internal value of the option |
options[].val |
string | Yes | Display text of the option |
options[].default |
boolean | No | Whether this is the default selection |
use_radio |
boolean | No | Display as radio buttons instead of dropdown |
radio_separator |
string | No | Separator between radio buttons |
Checkbox (ext_type: 5, type: "checkbox")
Multiple-choice checkbox field. options is required.
{
"ext_title": "Tags",
"ext_type": 5,
"type": "checkbox",
"options": [
{ "key": "important", "val": "Important" },
{ "key": "featured", "val": "Featured", "default": true },
{ "key": "archived", "val": "Archived" }
],
"use_multiselect": false
}
| Parameter | Type | Required | Description |
|---|---|---|---|
options |
array | Yes | List of choices (minimum 1) |
options[].key |
string | Yes | Internal value of the option |
options[].val |
string | Yes | Display text of the option |
options[].default |
boolean | No | Whether this is selected by default |
use_multiselect |
boolean | No | Use multi-select widget instead of checkboxes |
checkbox_separator |
string | No | Separator between checkboxes |
Boolean (ext_type: 36, type: "bool")
Boolean (ON/OFF) toggle field. Displayed as a checkbox.
{
"ext_title": "Published",
"ext_type": 36,
"type": "bool",
"default_value": "false"
}
| Parameter | Type | Description |
|---|---|---|
default_value |
string | Default value: "true", "false", or "" |
Image Upload (ext_type: 4, type: "image")
Image upload field.
{
"ext_title": "Main Image",
"ext_type": 4,
"type": "image",
"required": true,
"extensions": ["jpg", "jpeg", "png", "gif", "webp"],
"file_size": "5"
}
| Parameter | Type | Description |
|---|---|---|
extensions |
string[] | Allowed image file extensions |
file_size |
string | Maximum file size in MB |
ext_no_image_explain |
boolean | Hide the image description field |
File Upload (ext_type: 9, type: "file")
File upload field.
{
"ext_title": "Attachment",
"ext_type": 9,
"type": "file",
"file_type": ["pdf", "doc", "docx", "xlsx"],
"file_size": "10"
}
| Parameter | Type | Description |
|---|---|---|
file_type |
string[] | Allowed file extensions |
file_size |
string | Maximum file size in MB |
ext_no_file_name |
boolean | Hide the file name field |
Number (ext_type: 35, type: "number")
Numeric input field.
{
"ext_title": "Price",
"ext_type": 35,
"type": "number",
"required": true,
"searchable": true,
"min": 0,
"max": 1000000,
"number_type": "integer"
}
| Parameter | Type | Description |
|---|---|---|
min |
number | Minimum value |
max |
number | Maximum value |
number_type |
string | Number type: "integer" (integers only) |
Date (ext_type: 8, type: "date")
Date/datetime picker field.
{
"ext_title": "Publish Date",
"ext_type": 8,
"type": "date",
"required": true,
"add_time": true,
"placeholder": "YYYY-MM-DD"
}
| Parameter | Type | Description |
|---|---|---|
placeholder |
string | Placeholder text |
default_value |
string | Default value (supports strtotime format) |
add_time |
boolean | Include time picker |
Link (ext_type: 7, type: "link")
URL input field.
{
"ext_title": "External Link",
"ext_type": 7,
"type": "link",
"type_limitation": "url"
}
| Parameter | Type | Description |
|---|---|---|
type_limitation |
string | Validation type: "url" or "regex" |
regex |
string | Regex pattern (when type_limitation is "regex") |
Relation (ext_type: 20, type: "relation")
Reference to other records. module is required.
{
"ext_title": "Related Articles",
"ext_type": 20,
"type": "relation",
"module": "topics",
"group_id": "1",
"self_only": false
}
| Parameter | Type | Required | Description |
|---|---|---|---|
module |
string | Yes | Module to relate to: "topics", "member", "inquiry" |
group_id |
string | No | TopicsGroup ID (comma-separated for multiple) |
contents_type |
string | No | Category ID (comma-separated for multiple) |
has_permissions |
string | No | Apply permission settings |
self_only |
boolean | No | Show only records created by the current user |
secure_off |
boolean | No | Disable security checks |
order |
string | No | Sort order |
Location (ext_type: 11, type: "location")
Geolocation field (Google Maps integration).
{
"ext_title": "Store Location",
"ext_type": 11,
"type": "location",
"default_location": {
"lat": 35.6762,
"lng": 139.6503,
"address": "Shibuya, Tokyo"
}
}
| Parameter | Type | Description |
|---|---|---|
default_location |
object | Default location |
default_location.lat |
number | Latitude |
default_location.lng |
number | Longitude |
default_location.address |
string | Address |
JSON (ext_type: 28, type: "json")
JSON data input field.
{
"ext_title": "Metadata",
"ext_type": 28,
"type": "json",
"width": "100%",
"height": "300px",
"default_value": "{}"
}
| Parameter | Type | Description |
|---|---|---|
width |
string | Input width |
height |
string | Input height |
default_value |
string | Default JSON value |
Table (ext_type: 10, type: "table")
Table input field.
{
"ext_title": "Specifications",
"ext_type": 10,
"type": "table",
"rows": 5,
"cols": 3,
"cells": [
{ "key": "0_0", "val": "Property" },
{ "key": "0_1", "val": "Value" },
{ "key": "0_2", "val": "Notes", "is_locked": true }
]
}
| Parameter | Type | Description |
|---|---|---|
rows |
number | Number of rows |
cols |
number | Number of columns |
cells |
array | Cell configuration |
cells[].key |
string | Cell position key (e.g., "row_col") |
cells[].val |
string | Default cell value |
cells[].is_locked |
boolean | Make cell read-only |
cells[].is_separator |
boolean | Whether this is a separator row |
HTML (ext_type: 21, type: "html")
Raw HTML code input field.
{
"ext_title": "Custom HTML",
"ext_type": 21,
"type": "html",
"width": "100%",
"height": "300px",
"allow_all_tags": true
}
| Parameter | Type | Description |
|---|---|---|
width |
string | Input width |
height |
string | Input height |
default_value |
string | Default HTML value |
allow_all_tags |
boolean | Allow all HTML tags |
dont_use_editor |
boolean | Use plain text editor |
Counter (ext_type: 34, type: "counter")
Auto-increment counter. Used for sequential numbers (e.g., receipt numbers). Values are assigned automatically.
{
"ext_title": "Receipt Number",
"ext_type": 34,
"type": "counter",
"searchable": true
}
Only common properties are available for this field type.
Block Editor (ext_type: 38, type: "block_editor")
Block-based content editor.
{
"ext_title": "Page Content",
"ext_type": 38,
"type": "block_editor",
"allowed_blocks": ["paragraph", "heading", "image", "list"]
}
| Parameter | Type | Description |
|---|---|---|
default_value |
string | Default value |
allowed_blocks |
string[] | List of allowed block types |
Autocomplete (ext_type: 13, type: "textauto")
Text field with autocomplete functionality.
{
"ext_title": "Author Name",
"ext_type": 13,
"type": "textauto",
"topics_group_id": "1",
"field": "ext_col_01"
}
| Parameter | Type | Description |
|---|---|---|
topics_group_id |
string | Source TopicsGroup ID for suggestions |
field |
string | Field name to display as suggestions |
placeholder |
string | Placeholder text |
default_value |
string | Default value |
File Storage Fields
Amazon S3 File (ext_type: 27, type: "s3file")
{
"ext_title": "S3 File",
"ext_type": 27,
"type": "s3file",
"file_type": ["pdf", "zip"],
"file_size": "50"
}
Google Cloud Storage File (ext_type: 33, type: "gcsfile")
{
"ext_title": "GCS File",
"ext_type": 33,
"type": "gcsfile",
"file_type": ["pdf", "zip"],
"file_size": "50"
}
File Manager (ext_type: 30, type: "filemanager")
{
"ext_title": "File Manager",
"ext_type": 30,
"type": "filemanager",
"file_type": ["pdf", "doc"],
"file_size": "20"
}
Shared parameters for file storage fields:
| Parameter | Type | Description |
|---|---|---|
file_type |
string[] | Allowed file extensions |
file_size |
string | Maximum file size in MB |
Vimeo Video (ext_type: 31, type: "vimeo")
{
"ext_title": "Video",
"ext_type": 31,
"type": "vimeo",
"file_size": "100"
}
CSV Table (ext_type: 29, type: "csvtable")
{
"ext_title": "Master Data Selection",
"ext_type": 29,
"type": "csvtable",
"csvtable_group_id": "1",
"multiple": false
}
| Parameter | Type | Description |
|---|---|---|
csvtable_group_id |
string | CSV master group ID |
multiple |
boolean | Allow multiple selections |
CSV Table Checkbox (ext_type: 37, type: "csvtable_checkbox")
{
"ext_title": "Master Data Multi-Select",
"ext_type": 37,
"type": "csvtable_checkbox",
"csvtable_group_id": "1"
}
API Integration (ext_type: 32, type: "api")
External API integration field. api_settings is required.
{
"ext_title": "External Data",
"ext_type": 32,
"type": "api",
"api_settings": [
{
"title": "Product Search",
"url": "https://api.example.com/products?q={query}",
"path_to_list": "results",
"display_format": "{name} - ${price}",
"saved_format": "{id}",
"preview_url": "https://example.com/products/{id}"
}
],
"dialog_title": "Select a Product"
}
| Parameter | Type | Required | Description |
|---|---|---|---|
api_settings |
array | Yes | List of API configurations |
api_settings[].title |
string | No | Title of the API config |
api_settings[].url |
string | No | API URL |
api_settings[].path_to_list |
string | No | Path to the list in the response |
api_settings[].display_format |
string | No | Display format template |
api_settings[].saved_format |
string | No | Saved value format |
api_settings[].preview_url |
string | No | Preview URL template |
dialog_title |
string | No | Popup dialog title |
on_select_hook |
string | No | Hook function name on selection |
on_save_hook |
string | No | Hook function name on save |
on_popup_hook |
string | No | Hook function name on popup open |
Field Groups (Repeatable)
Field groups allow you to bundle multiple fields into a repeatable section.
Group Structure
{
"group_nm": "Group Name (required)",
"group_slug": "group-slug",
"group_repetitions": 5,
"fields": [
// Fields inside the group (each field must have repetitions: 1)
]
}
| Parameter | Type | Required | Description |
|---|---|---|---|
group_nm |
string | Yes | Group display name |
group_slug |
string | No | Group slug for API |
group_repetitions |
number | No | Number of allowed repetitions (default: 1, minimum: 1) |
fields |
array | Yes | Array of fields in the group (minimum 1) |
Important: Fields inside a group must have repetitions: 1.
Site-wide ceiling: the maximum group_repetitions a site allows is reported by whoami's site.limits.topics_ext_group_loop ({current, max}, max is 99). Don't assume a number — check whoami before setting a large group_repetitions. current is raisable up to max via admin_setting-update (see /kuroco-admin-mcp).
Group Example
{
"group_nm": "Product Variations",
"group_slug": "product-variation",
"group_repetitions": 10,
"fields": [
{
"ext_title": "Color",
"ext_type": 0,
"type": "text",
"repetitions": 1,
"required": true
},
{
"ext_title": "Size",
"ext_type": 2,
"type": "select",
"repetitions": 1,
"options": [
{ "key": "S", "val": "S" },
{ "key": "M", "val": "M" },
{ "key": "L", "val": "L" }
]
},
{
"ext_title": "Price",
"ext_type": 35,
"type": "number",
"repetitions": 1,
"min": 0
},
{
"ext_title": "Image",
"ext_type": 4,
"type": "image",
"repetitions": 1,
"extensions": ["jpg", "png", "webp"]
}
]
}
Practical Examples
Reminder: These examples set only
name/description/fields, so they inherit the recommendedcontent_input_type: 2— the body is an explicit field, as in Example 1'sArticle Body. Add the access-rule parameters from Content Definition Parameters whenever the request implies restricted visibility.
Example 1: News Articles
{
"name": "News Articles",
"description": "Content structure for a news website",
"fields": [
{
"ext_title": "Subtitle",
"ext_type": 0,
"type": "text",
"searchable": true,
"max_length": 200
},
{
"ext_title": "Category",
"ext_type": 2,
"type": "select",
"required": true,
"options": [
{ "key": "politics", "val": "Politics" },
{ "key": "economy", "val": "Economy" },
{ "key": "sports", "val": "Sports" },
{ "key": "entertainment", "val": "Entertainment" }
]
},
{
"ext_title": "Main Image",
"ext_type": 4,
"type": "image",
"required": true,
"extensions": ["jpg", "jpeg", "png", "webp"],
"file_size": "5"
},
{
"ext_title": "Article Body",
"ext_type": 6,
"type": "wysiwyg",
"required": true,
"height": "500px"
},
{
"ext_title": "Related Articles",
"ext_type": 20,
"type": "relation",
"module": "topics"
}
]
}
Example 2: Product Catalog
{
"name": "Products",
"description": "Product catalog for an e-commerce site",
"fields": [
{
"ext_title": "Product Code",
"ext_type": 0,
"type": "text",
"required": true,
"searchable": true,
"max_length": 50
},
{
"ext_title": "Price",
"ext_type": 35,
"type": "number",
"required": true,
"min": 0,
"number_type": "integer"
},
{
"ext_title": "Product Description",
"ext_type": 6,
"type": "wysiwyg",
"height": "300px"
},
{
"ext_title": "In Stock",
"ext_type": 36,
"type": "bool",
"default_value": "true"
},
{
"ext_title": "Tags",
"ext_type": 5,
"type": "checkbox",
"options": [
{ "key": "new", "val": "New Arrival" },
{ "key": "sale", "val": "On Sale" },
{ "key": "recommended", "val": "Recommended" }
]
},
{
"group_nm": "Product Images",
"group_repetitions": 5,
"fields": [
{
"ext_title": "Image",
"ext_type": 4,
"type": "image",
"repetitions": 1,
"extensions": ["jpg", "png", "webp"]
},
{
"ext_title": "Caption",
"ext_type": 0,
"type": "text",
"repetitions": 1
}
]
}
]
}
Example 3: Event Management
{
"name": "Events",
"description": "Event information management",
"fields": [
{
"ext_title": "Start Date/Time",
"ext_type": 8,
"type": "date",
"required": true,
"add_time": true
},
{
"ext_title": "End Date/Time",
"ext_type": 8,
"type": "date",
"add_time": true
},
{
"ext_title": "Venue Location",
"ext_type": 11,
"type": "location",
"default_location": {
"lat": 35.6762,
"lng": 139.6503,
"address": "Tokyo"
}
},
{
"ext_title": "Capacity",
"ext_type": 35,
"type": "number",
"min": 1,
"number_type": "integer"
},
{
"ext_title": "Registration Number",
"ext_type": 34,
"type": "counter"
},
{
"ext_title": "Details",
"ext_type": 6,
"type": "wysiwyg",
"height": "400px"
},
{
"ext_title": "Flyer",
"ext_type": 9,
"type": "file",
"file_type": ["pdf"],
"file_size": "10"
}
]
}
Important Notes
ext_typeis what determines the field type. If you also sendtype, it must be the matching pair (see the Field Type Reference table); omittingtypeis fineselectandcheckboxtypes requireoptions(at least one option)- A request about who can see or edit the content is a container parameter, not a field → Content Definition Parameters
- Read the record back after every write and confirm the fields were stored → Verifying the result
relationtype requiresmoduleapitype requiresapi_settings- Fields inside field groups must have
repetitions: 1 name(content structure name) and each field'sext_titlecannot be empty strings
Related Skills
/kuroco-content-structure-design- Design decisions to make before this skill (TopicsGroup splitting, master-data modeling, category/tag/relation choice, ext_slug policy)/kuroco-admin-mcp- Admin MCP connection setup, OAuth authentication, scopes, tool naming/kuroco-api-content- API design, authentication, and content CRUD operations/kuroco-app-builder- End-to-end app building workflow that uses this skill in its backend phase