Skip to content

Commit 84a8fef

Browse files
committed
docs(mcp): clarify singleSelect/multipleSelects field type normalization and choice handling
- Document that normalizeFieldType() translates public API names (singleSelect, multipleSelects) to internal names (select, multiSelect) automatically - Add singleSelect/multipleSelects typeOptions examples to skill template - Add "Working with Select Choices" section explaining choice merging pattern - Add pitfalls #10 and #11 warning against passing internal type names directly and partial choice lists
1 parent 268ff5d commit 84a8fef

3 files changed

Lines changed: 20 additions & 4 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Standalone users install via `npx airtable-user-mcp` or `npm i -g airtable-user-
5656
Key files:
5757
- `src/index.js` — MCP server entry point, tool registration
5858
- `src/auth.js``AirtableAuth` class, browser launch, CSRF/secretSocketId capture
59-
- `src/client.js``AirtableClient`, wraps internal API with caching
59+
- `src/client.js``AirtableClient`, wraps internal API with caching. `normalizeFieldType()` translates public-API type names to internal names before every request: `multipleSelects``multiSelect`, `singleSelect``select`. Always use the public names (`singleSelect`, `multipleSelects`) in tool calls — never the internal names. Choices arrays are also normalised here from `[{ name, color }]` to the keyed-object format the internal API requires.
6060
- `src/cache.js` — request cache
6161
- `src/login.js` — interactive CLI login
6262
- `src/login-runner.js` — programmatic login spawned by extension host

packages/extension/src/skills/templates/skillTemplates.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ Non-destructive field operations.
428428
| \`create_field\` | Create any field type (text, number, checkbox, formula, rollup, lookup, count, …). |
429429
| \`create_formula_field\` | Shorthand for creating a formula field. |
430430
| \`update_formula_field\` | Update the formula text of an existing formula field. |
431-
| \`update_field_config\` | Update config of any computed field (rollup, lookup, count, formula). |
431+
| \`update_field_config\` | Update config of any field type — computed (rollup, lookup, count, formula) or non-computed (singleSelect, multipleSelects, number, date, …). |
432432
| \`rename_field\` | Rename a field. |
433433
| \`update_field_description\` | Set or clear a field's description/documentation. |
434434
| \`duplicate_field\` | Clone a field. Pass \`duplicateCells: true\` to copy values too. |
@@ -454,8 +454,22 @@ Non-destructive field operations.
454454
| rollup | \`{ fieldIdInLinkedTable, recordLinkFieldId, resultType, referencedFieldIds }\` |
455455
| lookup | \`{ recordLinkFieldId, fieldIdInLinkedTable }\` |
456456
| count | \`{ recordLinkFieldId }\` |
457+
| singleSelect | \`{ choices: [{ name: "Option A", color: "blueLight2" }] }\` |
458+
| multipleSelects | \`{ choices: [{ name: "PC" }, { name: "Xbox", color: "greenLight2" }] }\` |
457459
| text, number, checkbox | \`{}\` (no typeOptions needed) |
458460
461+
#### Working with Select Choices
462+
Pass choices as an array of \`{ name, color? }\` objects — IDs are auto-generated for new choices.
463+
464+
To **add choices without losing existing ones**, call \`get_table_schema\` first to get existing choice IDs, then pass the full merged list:
465+
\`\`\`json
466+
{ "choices": [
467+
{ "id": "selXXXXXXXXXXXXXX", "name": "Existing Choice" },
468+
{ "name": "New Choice", "color": "pinkLight2" }
469+
] }
470+
\`\`\`
471+
Choices **not** in the list are deleted. Omitting \`id\` creates a new choice.
472+
459473
---
460474
461475
### Category 6: Field Destructive (2 tools)
@@ -797,4 +811,6 @@ and use airtable-user-mcp \`query_records\` to read/search data (especially when
797811
7. **Using \`emptyGroupState: "visible"\` in \`update_view_group_levels\`** — the API rejects this value; omit the field (defaults to "hidden")
798812
8. **Using REST API \`filterByFormula\` with \`FIND()\`/\`SEARCH()\` on lookup fields** — returns wrong/empty results; use \`query_records\` with \`search\` instead
799813
9. **Using Official MCP to read records when lookup field values matter** — Official MCP may return unresolved IDs; \`query_records\` returns fully resolved strings
814+
10. **Passing \`fieldType: "singleSelect"\` to \`create_field\` / \`update_field_config\` and expecting raw API type matching** — the internal Airtable API uses \`"select"\` (not \`"singleSelect"\`); airtable-user-mcp normalises this automatically so always pass \`"singleSelect"\` — never pass \`"select"\` directly
815+
11. **Passing a partial choices list to \`update_field_config\` on a select field** — choices NOT included are deleted; always fetch existing choice IDs first with \`get_table_schema\` and include them in the list
800816
`;

packages/mcp-server/src/client.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,10 @@ function normalizeFieldType(type, typeOptions = {}) {
319319
typeOptions: { ...opts, ...(opts.choices ? { choices: normalizeChoices(opts.choices) } : {}) },
320320
};
321321
}
322-
// Normalize singleSelect choices to object form (type name kept as-is until confirmed).
322+
// "singleSelect" is the public/REST API name; internal API uses "select" (mirrors multipleSelects → multiSelect).
323323
if (type === 'singleSelect' || type === 'select') {
324324
return {
325-
type,
325+
type: 'select',
326326
typeOptions: { ...opts, ...(opts.choices ? { choices: normalizeChoices(opts.choices) } : {}) },
327327
};
328328
}

0 commit comments

Comments
 (0)