surveycto
Skill10 starsChanged 5 months agoApache-2.0View on GitHub
Design, edit, debug, and convert SurveyCTO forms (XLSForm .xlsx with survey/choices/settings sheets), server datasets (.xml with a dataset root element), Data Explorer workbook definitions, and field plug-ins (.fieldplugin.zip bundles attached via `custom-name` appearances). Covers form logic, expressions, choice lists, repeat groups, skip patterns, constraints, calculations, dataset publishing, and case management. Also covers converting forms from KoboToolbox/ODK XLSForms, CommCare XForms XML, Qualtrics .qsf JSON, and other sources into SurveyCTO XLSForms. Use when the user mentions SurveyCTO, XLSForm, ODK-based forms, survey forms, or data collection forms; when working with relevant .xlsx, .xml, or .fieldplugin.zip files; or when converting a form from another platform. New XLSForms always start from the bundled template at assets/xlsform-template.xlsx — never built from scratch.
1 other project uses a version of this.
Adopted by
Most recently taken up by
- andrewzeitlin/claude_skillstheir own version, first seen Oct 8, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- andrewzeitlin/claude_skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repositoryOne other file it gives its agents.
- AGENTS.mdAGENTS.md
The file
Show its rules and text
We split this skill into the rules it gives an agent
32 rules. About 0 are common, 13 are distinctive, and 19 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈13
Distinctive · estimate
This author's own choices. Worth a look.
19
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 48
Before choosing tools, before writing code, before reading further:
- Very specific: line 52
NEVER: create an .xlsx from scratch.
- Very specific: line 72
Before any MCP tool call: read references/mcp.md, then call get_surveycto_mcp_capabilities when unsure to learn the current tool list and …
- Distinctive: line 74
Create a new XLSForm: follow the Workflow: create a new form below — it is the canonical sequence and enforces the template-first rule.
- Distinctive: line 90
If you can fetch URLs, the most authoritative live sources are:
- Distinctive: line 96
Prefer these over the bundled primers when verifying current product behavior.
- Distinctive: line 110
When the user asks about SurveyCTO product behavior, conventions, or documentation, prefer sources in this order:
- Very specific: line 112
MCP
kb_search/get_surveycto_primer: if available — current, indexed, authoritative. - Very specific: line 178
Use single quotes for string literals: ${field} = 'yes'
- Very specific: line 213
Every new XLSForm must start from the bundled template at assets/xlsform-template.xlsx.
- Very specific: line 217
If MCP tools are available, pass the copied file to start_xlsform_session via the upload URL flow and apply edits with xls_apply_patches.
- Distinctive: line 218
If MCP tools are unavailable, open the copied file and edit it in place — add your content into the existing worksheets.
- Very specific: line 221
Do NOT create a new .xlsx file from scratch.
- Distinctive: line 222
Do NOT create new worksheets and build the structure yourself.
- Distinctive: line 223
Do NOT delete and recreate worksheets from the template.
- Distinctive: line 224
Do NOT "rebuild" the form "properly" — the template IS the proper starting point.
- Distinctive: line 228
Never run any of these for a new form:
- Distinctive: line 239
Follow these steps in order:
- Very specific: line 243
If MCP tools are available, read references/mcp.md, then upload the copied file via start_xlsform_session.
- Very specific: line 244
Read the starting structure fully before editing.
- Very specific: line 271
Only write to rows that contain or will contain data: Don't touch unused rows below your content.
- Very specific: line 292
Add a field: write type, name, and label (at minimum) into the next empty row in survey.
- Very specific: line 293
Add a choice list: write list_name, value, label rows in choices.
- Very specific: line 296
Add skip logic: write the expression into the relevance column on the target row.
- Very specific: line 297
Add a constraint: write the expression into constraint (use .
- Very specific: line 361
Default to native field types and appearances.
- Distinctive: line 377
Follow the decision order above.
- Distinctive: line 388
Real-device pass: before going live: SurveyCTO Collect on a representative Android device, Collect for iOS, and at least one desktop …
- Very specific: line 405
Create a SERVER dataset with the lookup data.
- Very specific: line 407
Use pulldata('dataset_id', 'column_to_return', 'lookup_column', ${key_field}) in a calculation.
- Very specific: line 411
Create a SERVER dataset and attach it to the form via <formLinks>.
- Very specific: line 424
Before adding a plug-in to a form, confirm with the user that the added complexity is worth the gained functionality, and remind them they …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
SurveyCTO Form, Plug-in, and Dataset Authoring
Skill version: 1.0.0-beta.9. SurveyCTO MCP tool responses publish the current skill-version policy: start_xlsform_session returns a skill_advisory, and get_surveycto_mcp_capabilities returns intended_skill.versions. When you see one, compare it against this skill's version above (semantic-version ordering, pre-release aware) and act: if this version is below the response's deprecated_below_version, warn the user that their installed SurveyCTO skill is deprecated and should be updated now from the response's download_url; if it is below recommended_min_version, mention a newer version is available and offer to help update. Say nothing about versions when this skill is current, and raise it at most once per conversation. Skills do not auto-update, so this is the only update signal users get. See references/mcp.md for the exact comparison rules.
SurveyCTO is a mobile data collection platform built on the XLSForm and ODK standards, with platform-specific extensions and divergences. This skill provides SurveyCTO domain expertise for the four definition file types you may encounter:
- XLSForm form definitions (
.xlsx) — survey instruments - Dataset definitions (
.xml) — server dataset structure and publishing rules - Data Explorer workbook definitions (
.xlsx) — monitoring dashboards - Field plug-in bundles (
.fieldplugin.zip) — custom HTML/CSS/JS that takes over rendering of a single field
For a high-level orientation, read references/overview.md first.
Identifying SurveyCTO files
| File type | Format | How to recognize |
| XLSForm | .xlsx | Worksheets named survey, choices, settings |
| Dataset definition | .xml | Root element <dataset> with <definition> child |
| Data Explorer workbook | .xlsx | Worksheets named summaries, settings, global_filters, global_exclusions |
| Field plug-in bundle | .fieldplugin.zip | Zip with manifest.json, template.html, style.css, script.js at the root; referenced from a field's appearance as custom-<name> |
XLSForm starting point
Before choosing tools, before writing code, before reading further:
- New form → Step 1 is ALWAYS a literal file copy from
assets/xlsform-template.xlsxto the output path (for example,cp assets/xlsform-template.xlsx <output_path>in a Unix shell). The template is bundled with this skill atassets/xlsform-template.xlsx(resolve relative to the skill's root directory). Then proceed with the tooling rules below, uploading or opening the copied file. - Existing form → load the user's workbook as-is; do not regenerate it.
- NEVER create an
.xlsxfrom scratch. This rule applies regardless of which tools are available — it is not an MCP-only requirement and there is no fallback exemption. See Anti-pattern: building the workbook from scratch for the specific tools and idioms to avoid.
This rule is the most commonly-skipped step in this skill. If you are about to call openpyxl.Workbook(), pandas.ExcelWriter against a new path, libreoffice --headless to create a new file, or any code that writes XLSX bytes from scratch, stop and copy the template instead.
Tools you may have available
This skill is fully usable with no special tools — you have enough SurveyCTO knowledge embedded here and at the live documentation links to advise the user, describe edits in prose, and reason about forms. If additional tools are present, use them in the priority order below.
Tool-selection rule for XLSForm work: if the SurveyCTO MCP server tools are connected, you MUST use them for nontrivial XLSForm inspection, editing, validation, and export. Do not choose generic bash, python, openpyxl, LibreOffice, or spreadsheet tooling as the primary authoring path when SurveyCTO MCP tools are available. Generic tooling is only a fallback if MCP tools are unavailable, disconnected, or return an unrecoverable error.
SurveyCTO MCP server (preferred when available)
The SurveyCTO MCP server is a public, no-auth MCP server with capabilities built for this domain (XLSForm session inspect/edit/export and SurveyCTO knowledge-base search). Endpoint: https://assistant-be.surveycto.net/mcp (Streamable HTTP, stateless, no auth). If the server isn't connected when the user is editing XLSForms or asking factual SurveyCTO questions, mention it exists and offer to help install it; don't insist.
If the MCP tools are connected but XLSForm uploads or downloads are failing with network errors, see references/install.md for details to guide the user. Claude's code-execution network settings and Codex's command-sandbox permissions are separate from the MCP connection; verify file-transfer access before retrying. After changing Claude's egress settings, start a fresh chat and repeat the upload preflight.
Before using any SurveyCTO MCP tool, read references/mcp.md. It is mandatory for MCP usage; do not guess tool signatures, patch semantics, concurrency rules, error handling, or limits from this abbreviated overview. Read its "Preflight" section before your first XLSForm session in a chat; if the preflight fails, fix egress before proceeding.
Recommended workflow
- Before any MCP tool call: read
references/mcp.md, then callget_surveycto_mcp_capabilitieswhen unsure to learn the current tool list and primer topics. If the work involves XLSForms, you must also have readreferences/xlsform.mdfirst — see Prerequisite: read the XLSForm reference. - Factual SurveyCTO questions:
kb_search→ quote the returned URLs in your answer. - Create a new XLSForm: follow the Workflow: create a new form below — it is the canonical sequence and enforces the template-first rule.
- Add, update, or verify form-label translations: see Workflow: translate form labels below, which points to
references/translation.md. - Inspect or edit an existing XLSForm:
- Load the user's workbook (do not regenerate).
start_xlsform_sessionfor a fresh upload, orget_xlsform_summaryif resuming an existingsession_id. - Take a starting inventory from
form_summarybefore patching or paging rows. Note existing column names, choice lists (especially reusable ones likeyesno), settings values, and any warnings. Do not assume spellings or values from memory. xls_get_rows/xls_get_rowto inspect rows you intend to touch. Parallel calls are fine.xls_apply_patches— batch all related edits into one call. Usevalidate_only=trueon the full batch for risky changes. Update settings viachange_setting, notedit_row. For large batches setreturn_form_summary=falseandinclude_details=false, and trust a successful response — verify once at the end of the build, not after each batch (seereferences/mcp.md→ Trust patch success; verify once at the end).export_xlsformas soon as a form is in a deliverable state — especially when more forms remain in the same chat, so the user has each finished file in hand before you move on. Hand the file,download_url, or resource link to the user. Avoidformat="base64"for real workbooks. If the form references anycustom-<name>appearances, remind the user to attach the matching.fieldplugin.zipfiles in the SurveyCTO console at upload time — this skill and the MCP server only edit local files.- Usually leave the session open until TTL expiry; only call
end_xlsform_sessionfor explicit cleanup.
- Load the user's workbook (do not regenerate).
Generic spreadsheet/xlsx tooling
Use generic xlsx, Excel, Python/openpyxl, LibreOffice, or spreadsheet tools only when SurveyCTO MCP tools are not available or have failed in a way you cannot recover from. These tools can read and edit .xlsx files cell by cell, but they do not understand SurveyCTO form semantics, MCP session concurrency, or server-side formula recalculation. If you must use generic tooling, explicitly tell the user you are falling back because the MCP tools are unavailable or failed, then follow the editing rules in the Working with XLSForm .xlsx files section below.
Web fetch
If you can fetch URLs, the most authoritative live sources are:
- docs.surveycto.com — product documentation
- support.surveycto.com — Support Center articles
- www.surveycto.com — site/marketing content
Prefer these over the bundled primers when verifying current product behavior.
No file tooling at all
You can still help: describe edits in prose ("add a row to the survey worksheet with type=integer, name=age, label=Age in years, constraint=. >= 0 and . <= 120"), explain expressions, debug logic, and validate the user's draft against the rules below. The user can apply changes themselves.
Conserving context with sub-agents (when your host supports them)
If the host supports delegation (Cowork's Task tool, Claude Code's Task tool, etc.), use sub-agents to absorb read-heavy or context-bloating work when the parent agent does not need the raw tokens (only the conclusion). On long, multi-form builds in particular, context management is key.
Candidates for delegation: MCP egress preflight (the upload check), kb_search lookups, end-of-build verification (try not to re-verify after every batch; that anxiety loop is the proximate cause of context exhaustion on long builds). Do not delegate the actual xls_apply_patches writes. Patches must be serialized per session_id; running them inside a sub-agent the parent can't synchronize against invites session_conflict errors. The parent owns writes, sub-agents can own reads and summarization.
Authority hierarchy for knowledge questions
When the user asks about SurveyCTO product behavior, conventions, or documentation, prefer sources in this order:
- MCP
kb_search/get_surveycto_primerif available — current, indexed, authoritative. - Live web fetch of
docs.surveycto.com,support.surveycto.com,www.surveycto.comif you can. - Bundled primers in
references/— stable summaries, may lag the live docs. - General XLSForm/ODK knowledge — only with an explicit caveat that SurveyCTO has diverged in places (
=vs==,index()vsposition(),choice-label()vsjr:choice-name(),relevancecolumn name,selected()forselect_multiple, etc.).
Cite which level you're answering from when it matters.
XLSForm overview
An XLSForm is an XLSX workbook with three worksheets:
- survey — every question, group, repeat, and calculation is a row
- choices — choice lists for
select_oneandselect_multiplefields - settings — form-wide settings (title, ID, version, language, encryption)
Key columns (survey worksheet)
| Column | Purpose |
type | Field type: text, integer, decimal, select_one [list], select_multiple [list], note, calculate, date, geopoint, image, begin group, end group, begin repeat, end repeat, etc. |
name | Unique variable name (no spaces/punctuation); becomes the export column |
label | Prompt text (supports label:Language for translations) |
relevance | Skip logic expression controlling when the row appears |
constraint | Validation expression (. = current value) |
calculation | Expression for calculate fields |
choice_filter | Expression filtering choices for select fields |
appearance | Display options (minimal, quick, likert, field-list, etc.) |
repeat_count | Number of repetitions for repeat groups |
Key columns (choices worksheet)
| Column | Purpose |
list_name | Choice list name (referenced by select_one [list] / select_multiple [list]) |
value | Stored code for the choice |
label | Display text (supports label:Language) |
Core mechanics
${fieldname}references another field's value in expressions..refers to the current field's proposed value (used in constraints).- Groups (
begin group/end group) organize fields; arelevanceon the group controls all contents. - Repeats (
begin repeat/end repeat) repeat a block;repeat_countsets the number. - Field names must be unique across the entire form.
Full reference: references/xlsform.md.
SurveyCTO expression conventions
SurveyCTO uses XPath-like expressions with several platform-specific conventions. Always use these instead of standard ODK equivalents:
| Use this (SurveyCTO) | Not this (ODK/XPath) | Context |
= | == | Equality comparison |
index() | position() | Current repeat instance number |
choice-label() | jr:choice-name() | Get choice label text |
relevance column | relevant column | Skip logic column name |
div | / | Division operator in expressions |
Select field conventions
- For
select_one: both${field} = 'value'andselected(${field}, 'value')work. - For
select_multiple: always useselected(). The=operator does not work for multi-select.
String literals
Use single quotes for string literals: ${field} = 'yes'
Exception: when the string contains single quotes, use double quotes:
if(${yesno} = 1, "a string with 'quotes' in it", "no quotes")
Common expression patterns
Skip logic (relevance): ${consent} = 'yes'
Constraint with range: . >= 0 and . <= 120
Calculated age from date of birth: int((today() - ${dob}) div 365.25)
Conditional value: if(${gender} = 'female', 'F', 'M')
Pull data from attached dataset: pulldata('households', 'head_name', 'hhid', ${household_id})
Random assignment (calculate once): once(if(random() < 0.5, 'treatment', 'control'))
Full expression reference: references/expressions.md. Live documentation: Using expressions in your forms.
Working with XLSForm .xlsx files
This section applies regardless of which file-tooling path you're using (SurveyCTO MCP server, generic xlsx tool, or describing edits in prose for the user to apply). The rules are the same; only the mechanism differs. When MCP tools are connected, use this section to plan and validate edits, but apply the edits through xls_apply_patches and export through export_xlsform.
Prerequisite: read the XLSForm reference
Before any XLSForm work — creating, editing, debugging, or answering questions about an XLSForm — read references/xlsform.md. This is mandatory regardless of which tooling path you use (MCP, generic xlsx tooling, or prose-only) and regardless of how simple the task seems. It documents SurveyCTO-specific column conventions, multi-language structure, group/repeat rules, and expression syntax that diverge from general ODK knowledge — do not rely on general ODK memory, even for "obvious" things. If anything remains unclear after reading the reference, use kb_search (or live docs at docs.surveycto.com) before proceeding.
One known-recurring gotcha worth flagging up front, because general ODK memory consistently gets it wrong: in multi-language forms, the default language goes in the unsuffixed column (label, hint, constraint message, …) and additional languages go in suffixed columns (e.g., label:Spanish). Putting the default language in a suffixed column and leaving the base empty silently breaks the form. Other divergences are documented in references/xlsform.md; read it.
For translation work specifically, see Workflow: translate form labels and references/translation.md.
CRITICAL: Always start from the template
Every new XLSForm must start from the bundled template at assets/xlsform-template.xlsx.
How to use the template:
- Make a literal file copy from
assets/xlsform-template.xlsxto the output path (for example,cp assets/xlsform-template.xlsx <output_path>in a Unix shell; resolveassets/relative to the skill's root directory). Do not regenerate the file. - If MCP tools are available, pass the copied file to
start_xlsform_sessionvia the upload URL flow and apply edits withxls_apply_patches. - If MCP tools are unavailable, open the copied file and edit it in place — add your content into the existing worksheets.
Never do any of the following:
- Do NOT create a new
.xlsxfile from scratch. - Do NOT create new worksheets and build the structure yourself.
- Do NOT delete and recreate worksheets from the template.
- Do NOT "rebuild" the form "properly" — the template IS the proper starting point.
Anti-pattern: building the workbook from scratch
Never run any of these for a new form:
from openpyxl import Workbook; wb = Workbook()(or any other code that constructs a fresh workbook object and addssurvey/choices/settingssheets to it)pandas.ExcelWriter(<new_path>)orpandas.DataFrame.to_excel(<new_path>)against a path that doesn't already contain the template's contentslibreoffice --headless --convert-to xlsxstarting from a non-template source- Writing XLSX bytes directly (zipfile +
xl/worksheets/*.xml)
These produce a technically valid but unusable form: no conditional formatting, no help-survey/help-choices/help-settings sheets, no caseid row, no hidden audit calculations, no reusable yesno choice list, no auto-updating version formula, and no color-coded row styling. The user has to redo the work in Excel by hand. Copy assets/xlsform-template.xlsx instead — this is true even when MCP tools are unavailable and you fall back to openpyxl for editing. The fallback is for editing, not for creation from scratch.
Workflow: create a new form
Follow these steps in order:
- Make a literal file copy from
assets/xlsform-template.xlsxto the output path. Do not create a new workbook object, generate worksheets, or convert from another source. - Read
references/xlsform.mdbefore designing any form content. This is mandatory — not a lookup to do if confused later. Column conventions, multi-language structure, group/repeat rules, and expression syntax all have SurveyCTO-specific details that differ from general ODK knowledge. See Prerequisite: read the XLSForm reference. - If MCP tools are available, read
references/mcp.md, then upload the copied file viastart_xlsform_session. If MCP is unavailable, open the copied workbook directly with the best available spreadsheet/XLSX tooling. - Read the starting structure fully before editing. With MCP, use the returned
form_summary; without MCP, inspect the workbook sheets directly. Note next append location, exact column names already present, existing reusable choice lists, and current settings values. - Apply related edits in one batch. With MCP, use a single
xls_apply_patchescall andchange_settingforform_id,form_title,default_language, and/orinstance_name(the only patch-addressable settings keys); without MCP, write directly into the copied workbook's existing worksheets. - Export or save the result. With MCP, use
export_xlsformand hand thedownload_urlto the user. Remind the user to attach any required.fieldplugin.zipfiles in the SurveyCTO console at upload time.
Why the template is mandatory: It contains conditional formatting rules, help worksheets, column headers, starter metadata fields, formula-based versioning, and pre-formatted rows that cannot be reliably recreated programmatically. Skipping the template produces files that are technically valid but painful for humans to edit in Excel.
The template provides:
- survey worksheet with correct column headers and starter metadata fields, including hidden audit calculations and a
caseidrow - choices worksheet with headers and a
yesnochoice list - settings worksheet with headers and an auto-updating
versionformula - help-survey, help-choices, help-settings worksheets with reference documentation
- Conditional formatting rules that color-code rows by field type
Template starter content is a helpful starting point, not a fixed requirement. The starter rows (metadata fields, caseid, audit calculations) and the yesno choice list are included as conveniences for common cases. Keep standard metadata and audit rows by default because they are generally useful and not user-facing clutter; remove or replace them only when the source form or user request gives you a reason. Treat caseid as context-dependent: keep it for case-management work, but delete it when it would confuse a non-case draft. The yesno choice list is just an example choice list — drop or replace it freely when the form uses a different yes/no convention or no row references it. What's worth preserving is the tooling: the auto-updating version formula, the conditional formatting, the help worksheets, and the column-header structure. When you change starter content (e.g., rewrite a choice's value), be consistent — also update everything that references it.
Workflow: translate form labels
Adding a language, updating translations after the source changes, or verifying existing translations is its own workflow. Read references/translation.md before starting — don't improvise from the column-convention sketch in references/xlsform.md.
Workflow: convert a form from another platform
Converting a form definition exported from another data collection platform — KoboToolbox or ODK (XLSForm .xlsx), CommCare (XForms .xml), Qualtrics (.qsf JSON), or anything else — into a SurveyCTO XLSForm is its own workflow. Note that the conversion is best-effort and agent-driven, and the SurveyCTO MCP XLSForm tools work only on SurveyCTO-shaped XLSForms (do not point them at the source file). Read references/form-conversion.md before starting, then load the matching platform-specific reference (form-conversion-qualtrics.md, form-conversion-kobo.md, form-conversion-odk.md, form-conversion-commcare.md) if one exists for the source platform; for platforms without a dedicated reference, the base workflow still applies.
Editing rules (apply to every path)
- Preserve all existing worksheets — including
help-survey,help-choices,help-settingsand any other sheets present in the template or existing form. - Preserve existing formatting — do not clear or overwrite conditional formatting, column widths, or cell styles.
- Only write to rows that contain or will contain data. Don't touch unused rows below your content; don't write empty strings or
Noneto clear already-empty cells. - Append new rows after existing data. Do not assume a fixed starter-row count — the template ships with several pre-populated metadata and calculation rows (including hidden audit calculations and a
caseidrow), and an existing form may have any number of rows. With MCP,add_rowappends automatically; you do not need to compute a row index, but you can derive one fromform_summary(counts.n_rows, or the largestexcel_rowinsurvey_preview_rows) or by pagingxls_get_rowsif you need it. Without MCP, scan the workbook for the first row insurvey(andchoices) wheretype/list_nameand the surrounding columns are all empty, and append there. - Don't overwrite the
versioncell insettings— it contains an auto-updating formula. (The MCP server'sexport_xlsformrecalculates this for you.) - Use plain labels by default. SurveyCTO does not render Markdown in labels, hints, notes, or messages. Prefer plain text; use simple inline HTML only when it materially helps. See Label, Hint, And Message Formatting.
Editing workflow
- Start from the right base — copy the template (new forms) or load the existing workbook.
- Read
references/xlsform.mdbefore writing any patches. This applies to edits of existing forms just as much as new ones. Do not rely on general ODK memory for column names, expression syntax, or multi-language conventions. See Prerequisite: read the XLSForm reference. - Read the existing structure so you know what's there.
- With MCP available: upload first with
start_xlsform_session, then use the returnedform_summaryas the starting overview. Callxls_get_rowsonly when you need more detail than the summary provides. - Without MCP: open and read all three sheets.
- With MCP available: upload first with
- Plan the edits — identify the rows to add/modify and the cell values.
- Apply — write cell values into the appropriate rows and columns.
- With MCP available:
xls_apply_patches. - Without MCP: write cells directly.
- With MCP available:
- Validate against the checklist below.
- Save / export — with MCP available,
export_xlsformruns formula recalculation and returns the file; without MCP, save the workbook and use the best available recalculation path.
Common operations
- Add a field: write
type,name, andlabel(at minimum) into the next empty row insurvey. Place it in the correct position relative to groups and repeats. - Add a choice list: write
list_name,value,labelrows inchoices. Ensurelist_namematches what's referenced in theselect_one/select_multipletype. - Add a group:
begin grouprow and matchingend grouprow with the samename. Place fields between them. - Add a repeat:
begin repeat/end repeatrows with matchingname. Setrepeat_countif needed. - Add skip logic: write the expression into the
relevancecolumn on the target row. - Add a constraint: write the expression into
constraint(use.for current value); write a user-facing message intoconstraint message. - Add a calculation: row with
type=calculate, uniquename, expression incalculation. - Update settings: with MCP, use
change_settingforform_title,form_id,default_language, and/orinstance_name(the only patch-addressable settings keys; other columns such aspublic_keyandsubmission_urlmust be written by hand to a downloaded workbook). Without MCP, write those values into row 2 ofsettings. Leaveversionalone.
Validation checklist
After editing, verify:
- All
namevalues are unique across the entire form - No
namecontains spaces or special punctuation - Every
begin grouphas a matchingend group(samename) - Every
begin repeathas a matchingend repeat(samename) - Groups and repeats are properly nested (no overlapping)
- Every
select_one [list]/select_multiple [list]references a list that exists inchoices - Every choice list has at least one row with a
valueandlabel ${fieldname}references in expressions point to fields that exist- Expressions use SurveyCTO conventions (
=not==,index()notposition(), etc.) - Labels, hints, notes, and messages use plain text or simple inline HTML fragments, not Markdown
settingshasform_titleandform_id- Every
custom-<name>appearance in the form has a corresponding<name>.fieldplugin.zipthat the user will attach to the form - When handing the form back to the user, explicitly remind them to attach every required
.fieldplugin.zipin the SurveyCTO console at upload time. The skill and MCP server only edit local files; they do not upload or attach anything for the user
Dataset XML definitions
Dataset definitions are XML files with a <dataset> root element. They define column structure, form attachments, and publishing rules.
Before reading, writing, or editing any dataset definition XML, read references/datasets-xml.md first. This is mandatory regardless of how simple the task seems. It documents the standard column sets per dataset type, the schema-enforced order of <definition> children, the mandatory children of the option blocks, and the idFormatOptions and table-view value rules that the schema does not enforce and that otherwise cause silent upload rejections. Do not author dataset XML from the summary below or from general knowledge alone.
Key elements
| Element | Purpose |
<id> | Dataset ID used in pulldata() and search() calls |
<title> | Display name |
<datasetType> | Always SERVER (legacy CLIENT/REPORT are never authored; see full reference) |
<fieldNames> | Comma-separated column names/order |
<formLinks> | Forms that attach this dataset for pre-loading |
<dataLinks> | Publishing rules (incoming from forms, outgoing to files) |
<caseManagementOptions> | Case management display and workflow config |
<uniqueRecordField> | Column used as unique key for upserts |
Critical notes
- Element names are case-sensitive (e.g.,
otherUserCode, nototherUsercode). - Forms in
<formLinks>and<dataLinks>must be deployed before uploading the definition. <showColumnsWhenTable>contains multiple<columnNames>child elements, not a comma-separated string.- After creating or editing any dataset XML, validate and self-correct before delivering. Run the bundled validator and fix what it reports. Read
references/dataset-validation.mdfor how to run it and how to act on its findings.
Full reference (read before any dataset XML work): references/datasets-xml.md.
Data Explorer workbook definitions
Data Explorer workbooks are XLSX files with four worksheets: summaries, settings, global_filters, global_exclusions.
The summaries worksheet defines visualizations. Each row is a summary with a type (text, categorical, numeric, temporal, scatterplot, crosstab, …) and a field reference. Groups use begin group / end group with matching label values.
Full reference: references/data-explorer.md.
Field plug-ins
A field plug-in is a .fieldplugin.zip bundle (HTML/CSS/JS at the zip root) that takes over the rendering of a single form field. Plug-ins are supported only on text, integer, decimal, select_one, and select_multiple fields, and they run inside SurveyCTO Collect (Android), SurveyCTO Collect for iOS, and web forms.
When to use a field plug-in
Default to native field types and appearances. Plug-ins add real cost — attachment management, version bumps, aggressive caching, and cross-platform (Android/iOS/web) testing — so reach for them only when a native field can't deliver the needed behavior or UX. Decide in this order:
- Native fields and appearances first. Most needs are met by built-in
type/appearancecombinations. - Use as-is from the field plug-in catalog. If a maintained catalog plug-in already does what's needed, attach it without authoring anything.
- Customize an existing plug-in. Download the closest catalog plug-in or SurveyCTO
baseline-*repo as a ZIP from GitHub (Code → Download ZIP), or clone/fork it if the user is comfortable with Git, then edit the four files at the bundle root. - Start from the bundled template.
assets/field-plugin-template/is an intentionally minimal text-only skeleton — useful for an offline starting point or the smallest possible reading surface, but it omits several behaviors thatbaseline-textships (see What the bundled template omits inreferences/field-plugins.md). Add those back manually if you need them.
Agent behavior rule: when a form could benefit from a plug-in, present the trade-offs (added complexity vs. functionality/UX gained) and confirm with the user before adding any plug-in dependency. Unless the user has explicitly directed plug-in use, do not add custom-<name> appearances or attach .fieldplugin.zip files unilaterally.
Use an existing plug-in in a form
- Attach the
.fieldplugin.zipto the form (Form Designer → Attachments, or as a regular form attachment when uploading the XLSForm). - On the field, set
appearancetocustom-<filenamestem>— the part of the zip filename before.fieldplugin.zip, e.g.myplugin.fieldplugin.zip→custom-myplugin— with optional(key=value, …)parameters.
Authoring (when the user wants to build a plug-in)
Follow the decision order above. Concretely:
- Preferred starting point: download the closest SurveyCTO baseline plug-in for the field type (
baseline-text,baseline-integer,baseline-decimal,baseline-select_one,baseline-select_multiple) — or a closely related catalog plug-in — as a ZIP from GitHub's Code → Download ZIP button, then customize it. If the user prefers a Git workflow, cloning or forking works equally well; otherwise no Git tooling is required. - Minimal/offline alternative: copy
assets/field-plugin-template/. It is original code, not a copy ofbaseline-text, and intentionally leaves out several baseline behaviors (media rendering, HTML-entity unescaping in label/hint, standardnumbers/numbers_decimal/numbers_phoneappearance handling, soft-keyboard invocation, defaultplaceholderfallback, read-only display of empty values). Seereferences/field-plugins.mdfor the full list. - Edit the four files at the bundle root:
manifest.json,template.html,style.css,script.js. Always use triple-brace Mustache for{{{LABEL}}}and{{{HINT}}}(they may contain HTML). - Re-package as
<name>.fieldplugin.zipwith all files at the zip root (subdirectories get flattened on upload). Bumpmanifest.versionon every re-upload.
Testing
- Preview by default while authoring. When you create or edit a plug-in inside this skill, render
assets/field-plugin-test-harness/preview.htmlimmediately so the user can see the result, and re-render after every revision. In agent UIs that support inline HTML/JS artifacts, serve it as an artifact pre-populated with the four core files; otherwise point the user at the file on disk. Also runvalidate.mjs(static checks on the bundle — manifest schema, file presence, naming rules) after every change. - Final validation in the in-product field plug-in console (required before deployment). In the SurveyCTO form designer, click Test (top-right toggle, or the Test button next to the form on the Design tab) to enter test view. Navigate to the field that uses the plug-in; an icon button appears on the left edge of the form area — click it to expand the console. It shows the plug-in's details (name, version, author, supported field types, filename), the current values of any dynamic parameters and metadata, and three editable boxes (HTML/CSS/JS) with a Reload button for previewing live code edits. Console edits are session-scoped and not saved to the
.zip; copy fixes back to your source files and re-upload. The local harness's stubs are not a substitute for the real Android/iOS/web runtimes. See Testing field plug-ins. - Real-device pass before going live: SurveyCTO Collect on a representative Android device, Collect for iOS, and at least one desktop browser for web forms.
Agent behavior rule: after creating or revising plug-in source files, proactively (a) re-render the harness so the user can see the current state, and (b) recommend the in-product field plug-in console as the required next step (with the one-line "Form Designer → Test → navigate to the field → click the plug-in console icon" reminder). Do not present the work as done before the user has had a chance to validate there.
For everything else — full manifest schema, form API (field properties, runtime CSS classes, provided/called JS functions), parameters and metadata model, Android-only intent and phone APIs, and common pitfalls — see references/field-plugins.md.
Common patterns
Cascading selects
Filter choices in one field based on the selection in another:
- In
choices, add afiltercolumn (e.g.,region) to the child list. - In
survey, setchoice_filteron the child field:region = ${region}.
Pre-loaded data lookup with pulldata()
- Create a
SERVERdataset with the lookup data. - Attach it to the form via
<formLinks>in the dataset definition (this is what pre-loads it onto devices). - Use
pulldata('dataset_id', 'column_to_return', 'lookup_column', ${key_field})in acalculation.
Dynamic select list from pre-loaded data
- Create a
SERVERdataset and attach it to the form via<formLinks>. - In
survey, settypetoselect_one listnameandappearancetosearch('dataset_id')(or a more specific search expression). - In
choices, add one row forlistnamewherevalueandlabelcontain column names from the dataset (not literal values).
See references/xlsform.md for search() syntax patterns.
Random sampling from a repeat group
- Inside the repeat:
calculatefield withcalculation = once(random()). - Outside the repeat:
rank-index(1, ${random_calc})returns the index of the randomly-selected instance.
Use a custom field plug-in
Before adding a plug-in to a form, confirm with the user that the added complexity is worth the gained functionality, and remind them they will need to attach the .fieldplugin.zip themselves when uploading the form.
Attach the .fieldplugin.zip to the form, then set appearance on the field to custom-<filenamestem> with any parameters the plug-in expects. The <filenamestem> is the part of the zip filename before .fieldplugin.zip (for example, phonenumber.fieldplugin.zip → custom-phonenumber). The name field inside manifest.json is a human-readable display name and does not drive the appearance.
| type | name | appearance |
text | phone | custom-phonenumber(country='US', placeholder=${default_format}) |
Parameter values are evaluated by the form before the plug-in loads, so ${field} references and full SurveyCTO expressions work. In the plug-in, read parameters with getPluginParameter('country') rather than indexing fieldProperties.PARAMETERS. Full reference: references/field-plugins.md.
Publishing form data to a dataset
In the dataset XML, add a <dataLink> with:
<dataLinkClass>FORM</dataLinkClass><dataLinkType>INCOMING</dataLinkType><linkObjectId>form_id</linkObjectId><fieldMap>with JSON mapping form fields to dataset columns
Debugging common issues
| Symptom | Likely cause | Fix |
| Form won't upload | Duplicate name values | Search for duplicates in the name column |
| Form won't upload | Unbalanced groups/repeats | Check every begin/end pair |
| Field never appears | Incorrect relevance expression | Verify syntax; verify referenced fields exist |
| Constraint never passes | Wrong use of . vs ${fieldname} | In constraints, . is the current field; ${fieldname} is another field |
| Choices missing | list_name mismatch | select_one [list] must match a list_name in choices |
select_multiple logic fails | Using = instead of selected() | Always use selected(${field}, 'value') |
| Date comparison fails | Comparing string to date | Wrap with date(): ${field} > date('2024-01-01') |
| Dataset won't upload | Referenced form not deployed | Deploy all forms in <formLinks> and <dataLinks> first |
| Dataset element ignored | Case-sensitivity error | Check exact casing (e.g., otherUserCode) |
Plug-in field doesn't appear / custom-<name> ignored | Plug-in .fieldplugin.zip not attached, name mismatch, or unsupported field type | Re-attach the zip; confirm the custom-<name> token matches the .fieldplugin.zip filename stem (e.g. myplugin.fieldplugin.zip → custom-myplugin), not manifest.name; plug-ins only work on text/integer/decimal/select_one/select_multiple |
| Plug-in answer not saved | setAnswer not called, or select_multiple value joined with commas | Wire input events to setAnswer(value); for select_multiple, pass a space-separated list |
| Plug-in attachments 404 at runtime | Files placed in subdirectories inside the zip | Flatten the zip — every file at the root, no duplicate basenames |
References
| Primer | When to read |
references/overview.md | First — orientation, file types, how they fit together |
references/xlsform.md | Mandatory before any XLSForm work — column conventions, expressions, groups/repeats, multi-language |
references/translation.md | Adding, updating, or verifying form-label translations — workflow, preserve-verbatim rules, glossary handling, self-review, back-translation spot check, verification checklist |
references/form-conversion.md | Mandatory before any form conversion — platform-agnostic workflow for converting form definitions from other data collection platforms into SurveyCTO XLSForms, including the universal mapping concerns, the conversion-report contract, and pointers to platform-specific references |
references/form-conversion-qualtrics.md | Converting a Qualtrics .qsf JSON export — .qsf anatomy, question-type / selector mapping, display-logic translation, flow/branch handling, gotchas |
references/form-conversion-kobo.md | Converting a KoboToolbox XLSForm .xlsx — expression rewrites, Kobo-specific extensions (kobo--matrix, kobo--score, kobo--rank), settings differences |
references/form-conversion-odk.md | Converting an ODK XLSForm .xlsx — expression rewrites, column-name normalization (relevant→relevance), field-type and settings differences |
references/form-conversion-commcare.md | Converting a CommCare XForms .xml export — XForms anatomy, parsing helper, itext → label:Lang, case-management caveats |
references/expressions.md | Any expression work (relevance, constraint, calculation, choice_filter) |
references/datasets-xml.md | Server dataset XML definitions |
references/dataset-validation.md | Run after authoring/editing any dataset XML — validate the definition against the server's create/edit rules with the bundled assets/dataset-validation/validate_dataset.py, then self-correct |
references/data-explorer.md | Data Explorer dashboards |
references/field-plugins.md | Field plug-in authoring, packaging, form API, and testing |
Key documentation links
- Designing forms: core concepts
- Using expressions in your forms
- Grouping and repeating questions
- Using constraints to validate responses
- Implementing skip patterns with relevance
- Introduction to advanced dataset usage
- Working with server dataset XML files
- Advanced use of Data Explorer workbooks
- Using field plug-ins
- Field plug-in developer documentation (GitHub)
- SurveyCTO Support Center
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

