All notable changes to the "airtable-formula" extension will be documented in this file.
Check Keep a Changelog for recommendations on how to structure this file.
Promotes record templates to a first-class MCP capability. Templates are
the saved row scaffolds that Airtable surfaces under the "+ Add record"
flyout and inside the row-create extension — previously not addressable
through any public or internal API client we had. Discovered by capturing
the rowTemplate endpoints with pnpm capture:cdp:mutations while the
reporter exercised every template UI path.
New tools (9):
| Tool | Category | Purpose |
|---|---|---|
list_record_templates |
read | List record templates for a table |
create_record_template |
table-write | Create a template (client-side rtp... ID like view sections use vsc...) |
rename_record_template |
table-write | Rename a template |
update_record_template_description |
table-write | Set or clear a template's description |
set_record_template_cell |
table-write | Set a cell value in a template (static, linked rows, or linked templates) |
set_record_template_visible_columns |
table-write | Choose which columns the template surfaces in its UI |
duplicate_record_template |
table-write | Clone an existing template |
apply_record_template |
table-write | Apply a template — creates a new record using the template's cell defaults |
delete_record_template |
table-destructive | Delete a template |
Round-4 fixes (carried into 2.5.0 from interim 2.4.4 work):
set_view_columnsnow filters the primary column out of the move step before callingmoveVisibleColumns— Airtable rejects any move that displaces the pinned primary at index 0.reorder_view_fieldsmergePartialFieldOrderprotects the primary column at index 0; user-requested moves to index 0 are clamped to 1.update_view_group_levelsalways sendsemptyGroupState: 'hidden'; Airtable's API rejects'visible'with INVALID_REQUEST.list_tablesnow readsdata.tableByIdas a fallback whentableSchemas/tables/tableDatasare absent (matches the shape the reporter saw).update_view_filtersisWithincorrected: requirestimeZone(IANA) andshouldUseCorrectTimeZoneForFormulaicColumn: true, noexactDate, modes arethisCalendarMonth/thisCalendarYear(notthisMonth/thisYear). Tool description and AI skill template updated.
Counts: 52 → 61 tools. read-only profile: 8 → 9. safe-write
profile: 39 → 47. full profile: 52 → 61.
mcp-server: 2.4.4 → 2.5.0.
Probed the schema read response with a fresh patchright session against
a base that has sections (packages/mcp-server/dev-tools/debug-sections.js,
new pnpm probe:sections script). Found that sidebar sections live at
data.tableSchemas[N].viewSectionsById — NOT viewSections like the
WebSocket realtime delta model uses. 2.4.0 + 2.4.1 read the wrong key,
which is why the sections array came back empty.
listViewSections now reads from viewSectionsById first, falls back
to viewSections for fixture/test compatibility, falls back to the
2.4.2 ID-only-from-tableViewOrder path if both are absent. The rich
shape (name, viewOrder, pinnedForUserId, createdByUserId) is
fully restored on real bases.
mcp-server: 2.4.2 → 2.4.3.
After upgrading to 2.4.0 and running real end-to-end view-rollout work, the reporter surfaced two bugs in the new tooling. Both fixed here.
Bug 1 — set_view_columns orchestration 422'd at the move step (P1).
The internal step 3 looped per-id moveVisibleColumns([id], i) starting
at index 0. That fails reliably because grid views pin the primary
column at visible index 0 (you can't move anything else there) and
per-id moves of an already-correctly-positioned column also fail.
Steps 1 and 2 (hide-all + show-the-set) succeeded, so visibility was
correct but column order was untouched.
Fix: replace the loop with one batched moveVisibleColumns(visibleColumnIds, 1)
call — the entire ordered list is inserted starting at visible index 1,
after the pinned primary. Verified by the reporter's workaround snippet
on 11 grid views (100% success rate). Test updated to match.
Bug 2 — list_view_sections returned empty sections (P2, partial fix).
On the reporter's base, table.viewSections is absent from the cached
application/{appId}/read response even when tableViewOrder clearly
contains vsc... IDs. The full fix needs a network capture from a base
with sections to discover where the section objects actually live in
the schema response (none of our existing captures cover this — they
were either mutations-only or pre-section).
Partial fix shipped here: when viewSections is empty but
tableViewOrder contains vsc... IDs, surface them as bare-id entries
with name: null, viewOrder: null, partial: true, and a top-level
introspectionPartial: true flag with a introspectionNote explaining
the limitation. The agent at least knows which section IDs exist and
can pass them to move_view_to_section / rename_view_section /
delete_view_section (all of which still work for side effects).
mcp-server: 2.4.1 → 2.4.2.
Released hours after 2.4.0. The bundled extension copy of the MCP
server crashed immediately on every spawn with MODULE_NOT_FOUND for
../package.json, surfacing in MCP clients as transport error: transport closed. No tool ever ran from the bundled launcher.
Root cause: index.js resolved its own version with
require('../package.json'). That works when running from source
(packages/mcp-server/src/index.js → packages/mcp-server/package.json)
but fails when bundled to packages/extension/dist/mcp/index.mjs —
../package.json resolves to a non-existent
packages/extension/dist/package.json.
Fix: read version.json (which bundle-mcp.mjs writes alongside the
bundle) first, fall back to ../package.json for source/npx runs, fall
back to 'unknown' if both fail. Verified end-to-end against the bundled
launcher with a real initialize handshake.
Anyone whose MCP entries pointed at the bundled launcher
(~/.airtable-user-mcp/start.mjs → dist/mcp/index.mjs) was affected.
Standalone npx runs (npx -y airtable-user-mcp) were not — that path
keeps package.json adjacent. mcp-server: 2.4.0 → 2.4.1.
MCP Server 2.4.0 — Sidebar sections, view setup, non-grid metadata, user-report bug fixes (user report 2026-04-30)
16 net-new tools (52 total, was 36). All endpoints captured against Airtable's internal API on 2026-04-30 with pnpm capture:cdp:mutations.
View sections (sidebar grouping) — new view-section category, defaults on in safe-write:
list_view_sections— read all sections in a table with their view membership and the table-level mixedviewOrder(mixes view IDs and section IDs)create_view_section— generate avsc...ID and create a sectionrename_view_section— change a section's namemove_view_to_section— single tool covering four user actions: move view INTO section, move view OUT to ungrouped, reorder sections among each other, reorder views within a section. Maps to one Airtable endpoint (moveViewOrViewSection)delete_view_section— destroy a section. Verified behavior: views inside the section are NOT deleted — Airtable auto-promotes them into the table-levelviewOrderat the position the section used to occupy. Lives in the newview-section-destructivecategory, gated tofullprofile
View column setup — extend view-write:
set_view_columns— one-shot tool that hides every column, shows onlyvisibleColumnIds, places them in left-to-right order, and optionally setsfrozenColumnCount. Solves the user report's §1.4 "new views show all 168 fields, unusable until manually trimmed" problem in a single callshow_or_hide_all_columns— bulk on/off. Closes the §3.2 doc-promised-but-missing toolmove_visible_columns— move columns to a target index in the visible-only orderingmove_overall_columns— same, but in the overall (visible + hidden) ordering. Distinct from existingreorder_view_fields(which writes the full map) and frommove_visible_columns(different index space)update_frozen_column_count— set the frozen-column divider position
View presentation (Kanban / Gallery / Calendar) — extend view-write:
set_view_cover— set or clear the cover-image field and choosefitvscrop. Same endpoints work for both Kanban and Gallery (verified)set_view_color_config— apply a color config. Currently supportstype: "selectColumn"(cards colored by a single-select field's choice colors); other types pass through for forward compatibilityset_view_cell_wrap— toggle whether long values wrap (multi-line) or truncate (single-line)set_calendar_date_columns— setdateColumnRanges. Each entry is{ startColumnId }for single-point events or{ startColumnId, endColumnId }for ranges; the array form lets a Calendar overlay multiple date series at once
Form metadata (legacy form views only) — new form-write category, opt-in (gated to full profile since changes are visible to anyone with the form URL):
set_form_metadata— bundled tool that fans out to atomic Airtable endpoints fordescription,afterSubmitMessage,redirectUrl,refreshAfterSubmit,shouldAllowRequestCopyOfResponse,shouldAttributeResponses,isAirtableBrandingRemoved. Unset properties are not touchedset_form_submission_notification— per-user email-on-submit toggle (separate because it's per-user, not per-form)
Note on builder forms: Airtable's "Interfaces" / page-based forms (page/{pageId}/* endpoints, layout-engine element trees) are intentionally out of scope — they're a separate product surface that warrants a dedicated tool family. Filed as a follow-up.
Categories now in safe-write: read, table-write, field-write, view-write, view-section. Categories opt-in only via full or custom: *-destructive, view-section-destructive, form-write, extension.
isEmpty/isNotEmptyon text + formula(text) + lookup/rollup(text) fields — auto-rewritten to=/!=""before sending. The internal API rejects the documented operators withFAILED_STATE_CHECKon these field types; the rewrite happens client-side using the cached table schema (§2.1, §2.2)isEmpty/isNotEmptyon linked-record (foreignKey) fields — now throws a clear error pointing at the helper-formula workaround instead of letting Airtable's opaque422through (§2.3)- 3-level nested filter rejection —
updateViewFilterserrors now annotate the depth, the leaf operators that failed, and the recommended flatten pattern ((A AND B) OR (A AND C)instead ofA AND (B OR C)) (§2.4, §2.5) reorder_view_fieldsaccepts partial maps — pass only the field IDs you want to move; the tool reads the currentcolumnOrder, applies moves in ascending target-position order, and sends the complete map. Avoids theFAILED_STATE_CHECKAirtable's internal API returns for single-key payloads (§2.6)manage_toolsdiscoverability —ListToolsresponse now includes a dynamic description onmanage_toolslisting every tool hidden by the active profile (e.g.delete_table/delete_field/delete_viewinsafe-write).get_tool_statusalso returns adisabledByCategorysummary so an LLM can show the user what to enable (root-cause behind §1.2 — the delete tools always existed, they were just filtered)- Tool docstring audit — corrected
update_view_filters,show_or_hide_view_columns, andreorder_view_fieldsdescriptions to match actual behavior (§4.3)
- Formatter version setting was inert — Dashboard's "Formatter version" dropdown now actually switches engines. Extension consolidated on
airtableFormula.formula.formatterVersionand reads this key from all four load sites; legacybeautifierVersion/minifierVersionremain as fallback for user settings migrated from prior versions - Browser-choice changes didn't propagate to IDE MCP configs — Selecting a different browser (or picking a custom path) now re-writes the MCP entry for every already-configured IDE, so the new
AIRTABLE_BROWSER_CHANNEL/AIRTABLE_BROWSER_PATHenv vars take effect immediately instead of staying stale until the next Setup - Toggle MCP Tool Category command couldn't toggle table categories — Added
tableWriteandtableDestructiveto the command palette quick-pick; also fixed mis-mappedfieldWrite/viewWritefile keys that were breaking the label and tool-count display for those rows - Formatter commands missing from command palette —
beautify,minify,beautifyWithStyle,minifyWithLevel, andformatWithPresetare now contributed inpackage.json, with.formulalanguage enablement.beautifyFile/minifyFileadded to the explorer context menu for.formulafiles - Webview action cards weren't keyboard-accessible — All clickable
.action-carddivs are now<button>elements (or proper<a>for links); added:focus-visibleoutline and button resets so styling is preserved - Browser select had no way back to Auto — Added an "Auto (pick best available)" option to the Settings browser dropdown
- Dead "Docs" button on undetected IDE cards — Now links to each IDE's install/docs URL (Cursor, Windsurf, Claude Code/Desktop, Cline, Amp)
AIRTABLE_USER_MCP_HOMEadvertised but not honored — Introducedpackages/mcp-server/src/paths.jsas the single source of truth;auth.js,tool-config.js,health-check.js,login.js,login-runner.js,manual-login-runner.js, andcli.jsall read config and profile paths through it so the env var works everywhere, not just in the CLIstatus/doctorcommands- CI npm-pack smoke masked failures — Removed
|| truesonpm pack --dry-runfailures (missing files, malformed package.json) actually fail the job; switched toset -euo pipefailand a workspace-relative log file instead of/tmp/ - README screenshot TODOs — Removed placeholder comments in the root and mcp-server READMEs
- Incomplete URL injection guard —
renameTable,deleteTable,createView,updateFieldConfig, andrenameFieldnow validate Airtable IDs before URL construction (previously only early resolvers caught the issue) - Auth logout timer leak —
AuthManager.logout()now stops the auto-refresh timer before wiping credentials, eliminating ghost browser launches against a cleared profile - Dashboard logout false-success — Cancelling the logout confirmation modal now correctly reports cancellation to the webview instead of claiming success
manualTestLogPathsecret leakage — Debug mirror now scrubssecretSocketId, CSRF tokens, Bearer headers, passwords, OTP secrets, and cookies before writing to disk- Hung child-process cleanup — Auth manager's
_spawnScriptnow escalates toSIGKILL5 s afterSIGTERMif a child process ignores the termination signal - Non-atomic config writes —
tools-config.jsonis now written via a temp file + rename, preventing truncation on mid-write crashes - Debug tracer crash on circular refs —
trace()now uses a circular-safe stringify with a breadcrumb fallback so unusual tool arguments can't crash the server - Debug tracer error-message redaction — Thrown error messages routed through
traceToolHandlernow have bearer tokens, CSRF tokens, andsecretSocketIdvalues scrubbed - Weak request-ID randomness — Replaced
Math.random()withcrypto.randomBytesfor Airtable request IDs, entity IDs, andpage-load-idheaders _rawApiCallignoredmethodin JSON branch — JSON content-type calls were hardcoded to POST; now honor GET/PUT/PATCH/DELETE from the caller- Silent skill-install failure —
installSkills()during activation is now wrapped so failures log a warning instead of going unnoticed - Fallback
ToolProfileSnapshotwas missing new categories — AddedtableWrite/tableDestructivekeys to the DashboardProvider fallback, fixing a latent TS type error - Non-graceful MCP server shutdown —
SIGINT/SIGTERMnow run a bounded, idempotent shutdown (5 s timeout) that closes the transport and stops file watchers before exit, reducing Chromium orphans on Windows - Unbounded schema cache —
SchemaCachenow enforces a 50-entry LRU cap per map, preventing memory growth for long-running servers managing many bases check-tool-sync.mjscoverage gaps — Now also detects drift inBUILTIN_PROFILEScategory arrays andCATEGORY_LABELSkeys between the server and the extension mirror_apiCallcould stall forever — Added a hard 30 s wall-clock budget so a hung_recoverSessioncan't block queued requests indefinitely- Webview payload validation —
App.tsxnow runtime-checks incomingstate:update/auth:statemessages, silently dropping malformed payloads instead of crashing the React tree - Orphan browser on login failure —
login.jsnow wraps its main automation intry/finallyso selector misses and TOTP crashes always close the Chrome context - No rate limit on inbound tool calls — MCP server now caps concurrent tool calls (default 16,
AIRTABLE_MAX_CONCURRENT_TOOLSto override), preventing runaway LLM loops from flooding the auth queue - Accessibility — Dashboard tabs now use proper
role="tab"/role="tabpanel"/aria-selected; footer links, toggle switches, and buttons have explicitaria-labels for screen readers autoConfigureOnInstalldefault drift — Fixedpackages/extension/package.jsondefault fromfalsetotrueto matchsettings.tsand the webview store
- Hardcoded MCP version — Server now reads its version from
package.jsonat runtime instead of a stale constant - Settings default mismatch — Webview
autoConfigureOnInstalldefault now matches extension default (true) - Debug secret leakage — Debug tracer now redacts
_csrf,secretSocketId,password,otpSecret, and other credentials before writing to stderr - URL injection guard — All Airtable ID parameters are validated before interpolation into API URLs, preventing path-traversal attacks from malformed tool arguments
- CI matrix — GitHub Actions now runs on
ubuntu-latest,windows-latest, andmacos-latest - Build script robustness —
check-tool-sync.mjsnow uses brace-counting instead of a fragile regex to parse the TypeScript mirror
- Dual-channel publishing — MCP server now available as standalone
airtable-user-mcpnpm package - CLI subcommands —
npx airtable-user-mcp login,logout,status,doctor,install-browser - Dashboard version card — Shows extension + bundled MCP server versions with update-available hints
- Build-time version manifest — Fixes hardcoded
v2.0.0MCP version bug in dashboard - GitHub Actions CI/CD — Publish workflows for npm, VS Code Marketplace, and Open VSX
- Package rename — MCP server renamed from
mcp-internal-airtabletoairtable-user-mcp - Lazy-load patchright — Dynamic imports for browser deps (smaller install, no crash without browser)
- Command renames —
checkSession→status,downloadBrowser→install-browser
- Context menu submenus for beautify styles and minify levels
- Batch style/level commands for explorer selections
- Explorer file operations now use file-based formatting for better stability
- Minify in-place when running on
.min.formulaor.ultra-min.formula
- Beautifier v2 JSON performance improvements to prevent extension host freezes
- Vendor script resolution consistency across style/level commands
- Airtable-Matching Color Scheme: Exact syntax highlighting colors matching Airtable's interface
- Intelligent Diagnostics: Real-time error detection for unclosed parentheses, brackets, quotes
- IntelliSense Support: Auto-completion for all Airtable functions with parameter hints
- Version Selection: Choose between v1 (stable) and v2 (enhanced) for beautifier and minifier
- New Formatting Styles:
smartstyle (v2) andsafeminification level (v2) - Extended File Support:
.min.formulaand.ultra-min.formulaextensions - Enhanced Error Reporting: Better parenthesis mismatch detection with linked errors
- Default to v2 versions for both beautifier and minifier (with v1 fallback)
- Improved diagnostics to not flag
{}as errors (used in JSON string building)
- Syntax highlighting for minified files
- False positive errors for empty field references
{} - Better handling of long single-line formulas in minified files
- Initial release of Airtable Formula VS Code extension
- Beautify functionality: Format Airtable formulas with proper indentation and line breaks
- Minify functionality: Compress formulas to reduce size while maintaining functionality
- Syntax highlighting: Full support for
.formulafiles with custom language definition - Multiple formatting styles: Ultra-compact, compact, readable, JSON, and cascade styles
- Customizable settings: Configure indentation, line length, quote style, and minification levels
- Context menu integration: Right-click options for beautify/minify in formula files
- File operations: Batch beautify/minify operations on multiple files