Airtable Mcp Server

by Automations-Project

28 stars
165 downloads
Not rated
GitHub Website

About

VS Code extension and MCP server for Airtable, formula editor, schema tools, and 60+ automation utilities for bases, views, and fields.

Details

Author
Automations-Project
GitHub stars
28
Downloads
165
Categories
Developer Tools, Database, Other, Automation, Productivity

- 66 tools covering schema, views, fields, extensions, templates
- Browser-based login with SSO/2FA, credentials in OS keychain
- Full schema read including view filters, sorts, groups, descriptions
- Create formula, rollup, lookup, and count fields
- Create and configure all view types (grid, form, kanban, etc.)
- Duplicate records, views, and fields safely
- Validate formulas before applying them
- Tool profiles: read‑only, safe‑write, full, custom

Setting up with Highlight

This MCP is not yet compatible with Highlight’s one-click setup. However, you can still use it with Highlight by following these steps:

  1. Download and install Highlight from highlightai.com/download
  2. Navigate to the plugins tab and select "Add Custom Plugin"
  3. Configure the plugin with the settings below
    Plugin Name Airtable Mcp Server
    Command (node, npx, python, etc.)

    Please refer to the README for specific instructions on how to obtain API keys or other required environment variables.

  4. Enable "Start Automatically" if you want the plugin to start when Highlight launches

From the repository

Run npx -y airtable-user-mcp login for a one-time browser login. Then add it to your MCP client’s mcpServers configuration, for example:

{
"mcpServers": {
"airtable-user-mcp": {
"command": "npx",
"args": ["-y", "airtable-user-mcp"]
}
}
}

Use it alongside the official Airtable MCP to get full coverage; the server runs locally over stdio.

get_base_schema

Get the full schema of an Airtable base — all tables, fields (with typeOptions), and views in one call. Use this when you need fields or views; use `list_tables` when you only need table names/IDs (faster, lighter). Returns { tables: [...] }.

list_tables

List all tables in a base with their IDs and names — lightweight scaffolding call (no field data). Use this when you only need table IDs/names; use `get_base_schema` or `get_table_schema` when you also need fields or views. Returns [{ id, name }].

get_table_schema

Get the full schema for a single table — all fields (with typeOptions) and views. Use instead of `get_base_schema` when you only need one table (faster, less context). Use `list_fields` when you need fields only without view data. Returns { id, name, fields: [...], views: [...] }.

list_fields

List fields in a table — returns id, name, and type per field (lightweight by default). Use instead of `get_table_schema` when you need fields only (no view data). Set includeOptions=true to also return each field's full typeOptions (can be very large on wide tables — prefer the default for name/id/type lookups). Use `fieldType` or `nameContains` filters on large tables to reduce context size. Returns [{ id, name, type, typeOptions? }].

list_views

List all views in a specific table with their IDs, names, and types.

get_view

Read a view's live configuration from the base. Returns filters, sorts, groupLevels, columnOrder (rich per-column visibility + width), frozenColumnCount, colorConfig, metadata (view-type specific, e.g. gallery cover, calendar date field), rowHeight, description. Use this before update_view_filters / apply_view_sorts / update_view_group_levels to audit current state and choose between replace and append modes. Data source: internally hits /v0.3/table/{tableId}/readData with includeDataForViewIds=[viewId]. The application/read endpoint alone does NOT return filter/sort/group state — that's why the update tools need either "append" mode or a prior get_view call to merge safely. Fields: - filters: { filterSet: [...], conjunction: "and"|"or" } | null - sorts: [{ id, columnId, ascending }] | null (stored as lastSortsApplied internally) - groupLevels: [{ id, columnId, order, emptyGroupState }] | null - columnOrder: [{ columnId, visibility, width? }] - visibleColumnOrder: [columnId] — derived from columnOrder for convenience - metadata: type-specific config (gallery.coverColumnId, calendar.dateColumnId, etc.)

create_table

Create a new table in an Airtable base. Returns the generated table ID. The table starts with default fields (Name, Notes, Attachments, Status, etc.) — use list_fields after creation to inspect them.

rename_table

Rename a table in an Airtable base.

delete_table

Delete a table from an Airtable base. Requires both tableId AND the expected table name as a safety guard — refuses to delete if the name does not match. Airtable rejects deleting the last remaining table in a base.

list_record_templates

List all record templates for a table. Templates are embedded in the base scaffolding data. If the templates array is empty, pass debug:true and inspect the raw response to locate the templates key — the API path may vary by base.

create_record_template

Create a new record template for a table. Returns the generated templateId (rtp-prefixed). After creating, use set_record_template_cell to pre-fill field values.

rename_record_template

Rename an existing record template.

update_record_template_description

Set or update the description text of a record template.

set_record_template_cell

Pre-fill a field value on a record template. CELL OBJECT TYPES (verified via API capture 2026-05-01): Static value (text, number, boolean, single-select choice ID): { "type": "static", "value": "some text" } { "type": "static", "value": 42 } { "type": "static", "value": true } { "type": "static", "value": "selXXXXXXXXXXXXXX" } ← single-select: pass choice ID Linked record(s): { "type": "linkedRows", "value": [{ "foreignRowId": "recXXX", "foreignRowDisplayName": "Record Name" }] } To clear a field, omit the cellObject or pass null value.

set_record_template_visible_columns

Set which columns are shown (pre-fillable) on a record template. Pass an empty array to show all columns. isPartialSelection:true means only listed columns are shown; false means all are shown.

duplicate_record_template

Duplicate a record template within the same or a different table. Returns the new template ID.

apply_record_template

Apply (instantiate) a record template to create a new record pre-filled with the template's field values. Returns the new record data.

delete_record_template

⚠️ DESTRUCTIVE — Permanently delete a record template. This cannot be undone.

create_field

Create a new field in an Airtable table. Supports all field types including computed fields (formula, rollup, lookup, count) that are not available via the official API. FIELD TYPES (fieldType parameter): Supported names: "text", "multilineText", "number", "checkbox", "date", "singleSelect", "multipleSelects", "rating", "formula", "rollup", "lookup", "count" Friendly aliases (auto-normalized): "url" → type: "text" with validatorName = "url" "email" → type: "text" with validatorName = "email" "phone" / "phoneNumber" → type: "text" with validatorName = "phoneNumber" "dateTime" → type: "date" with isDateTime: true TYPE OPTIONS by fieldType: formula: { formulaText: "..." } rollup: { relationColumnId: "fldLINK", foreignTableRollupColumnId: "fldTARGET", formulaText: "SUM(values)" } (formulaText is REQUIRED — e.g. "SUM(values)", "COUNTA(values)", "IF(OR(values='X'),1,0)")) (old keys fieldIdInLinkedTable/recordLinkFieldId are auto-translated for backward compat) lookup: { relationColumnId: "fldLINK", foreignTableRollupColumnId: "fldTARGET" } (old keys fieldIdInLinkedTable/recordLinkFieldId are auto-translated for backward compat) count: { recordLinkFieldId } number (integer): { format: "integer", negative: false } number (currency): { format: "currency", symbol: "$", precision: 2, negative: false } number (percent): { format: "percentV2", precision: 2, negative: false } date / dateTime: { dateFormat: "Local"|"us"|"european"|"iso"|"friendly", timeFormat: "12hour"|"24hour", timeZone: "UTC"|"client"|<IANA-tz>, shouldDisplayTimeZone: true|false, isDateTime: true (auto for dateTime) } singleSelect: { choices: [{ name: "Option A", color: "blue" }], default: "selXXX" } multipleSelects: { choices: [{ name: "PC", color: "blue" }, { name: "Xbox", color: "cyan" }], default: ["selXXX"] } text / multilineText / checkbox / rating: omit typeOptions entirely — passing {} causes a 422 SELECT CHOICES: - Pass choices as an array [{ name, color? }] or as an object { selXXX: { name, color? } }. - The client auto-adds id inside each choice value, generates choiceOrder, and sets disableColors: false. - Color names (confirmed): "blue", "cyan", "teal", "green", "yellow", "orange", "red", "pink", "purple", "gray". - "default" sets the pre-selected value: string ID for singleSelect, array of IDs for multipleSelects. - To add/remove choices without losing existing ones, call get_table_schema first and include ALL choices in the update.

create_formula_field

Create a new formula field — shorthand for `create_field` with type "formula". Use `create_field` for all other field types (singleSelect, rollup, number, etc.). Returns { columnId }.

validate_formula

Validate a formula expression before creating or updating a formula field. Returns whether the formula is valid and what result type it produces (text, number, etc). Use this before create/update to catch errors early.

download_formula_field

Download the formula text of a formula field to a local file. Field refs are resolved to real field names ({Field Name}, Airtable's native syntax) so the file is readable and can be uploaded back unchanged. Writes a .formula file with a # AT: metadata header (appId, tableId, fieldId, fieldName) so the file can later be uploaded back with update_formula_field or the VS Code right-click command. When outputPath is omitted, returns the formula text without writing a file.

download_base_formulas

Download ALL formula fields from a base to local .formula files, organized into per-table subfolders. Field refs are resolved to real field names ({Field Name}, Airtable's native syntax) so files are readable and upload back unchanged. Each file includes a # AT: header with appId, tableId, fieldId, fieldName, description, and resultType. Tables with no formula fields are silently skipped. outputDir defaults to the current working directory when omitted.

update_field_config

Update the configuration of any field — computed OR non-computed. Works for formula, rollup, lookup, count, singleSelect, multipleSelects, number, date, text, and all other field types. COMMON typeOptions by fieldType: formula: { formulaText: "IF({Field}, 1, 0)" } rollup: { relationColumnId: "fldLINK", foreignTableRollupColumnId: "fldTARGET", formulaText: "SUM(values)" } (formulaText is REQUIRED; old keys fieldIdInLinkedTable/recordLinkFieldId auto-translated) lookup: { relationColumnId: "fldLINK", foreignTableRollupColumnId: "fldTARGET" } (old keys fieldIdInLinkedTable/recordLinkFieldId auto-translated) count: { recordLinkFieldId: "fldXXX" } singleSelect: { choices: [{ name: "Option A", color: "blue" }], default: "selXXX" } multipleSelects: { choices: [{ name: "PC", color: "blue" }, { name: "Xbox", color: "cyan" }], default: ["selXXX"] } number: { format: "integer"|"decimal"|"currency"|"percentV2", precision: 2, symbol: "$", negative: false } text / multilineText / checkbox: omit typeOptions entirely — passing {} causes a 422 SELECT CHOICES: - Pass choices as array [{ name, color? }] or object { selXXX: { name, color? } }. - Color names (confirmed): "blue", "cyan", "teal", "green", "yellow", "orange", "red", "pink", "purple", "gray". - "default" = pre-selected value: string ID for singleSelect, array of IDs for multipleSelects. ADDING TO AN EXISTING SELECT FIELD (merge, not replace): Choices not in the list are DELETED. To add without losing existing choices: 1. Call get_table_schema — each existing choice has { id, name, color } 2. Pass the full list: existing entries WITH their id, new entries WITHOUT: { choices: [{ id: "selXXXXXXXXXXXXXX", name: "Existing" }, { name: "New Choice", color: "pink" }] } REPLACING ALL CHOICES: just pass the new choices without any IDs.

update_formula_field

Update the formula body of an existing formula field — shorthand for `update_field_config` with type "formula". Automatically preserves existing format/precision typeOptions (e.g. percentV2, precision). Use `update_field_config` to change the field type or other typeOptions.

rename_field

Rename a field (column) in an Airtable table. Pre-validates the field exists before mutating.

delete_field

Delete a field from an Airtable table. Requires fieldId AND expectedName as a safety guard — deletion is refused if the name does not match. ⚠️ Irreversible: deleted field data is permanently lost and cannot be recovered. Always checks downstream dependencies first (formula fields, lookups, rollups referencing this field); returns dependency info without deleting unless force=true.

delete_fields

Delete multiple fields from an Airtable table in a single call. Each entry requires fieldId and expectedName as a safety guard (deletion is refused if names do not match). Fields are processed sequentially and all are attempted even if some fail — partial results are always returned. Optionally writes a JSON checkpoint file after each deletion so the batch can be resumed if interrupted.

create_view

Create a new view in an Airtable table. Optionally copy configuration from an existing view. View types: "grid", "form", "kanban", "calendar", "gallery", "gantt", "levels" (list view).

duplicate_view

Duplicate an existing view with all its configuration (filters, sorts, field visibility, etc).

rename_view

Rename a view.

delete_view

Delete a view from a table. Cannot delete the last remaining view in a table.

update_view_description

Update the description text of a view.

update_view_filters

Update the filter configuration of a view. Supports AND/OR conjunctions, nested filter groups, and Airtable's internal filter operators. FILTER FORMAT: Leaf filter: { columnId: "fldXXX", operator: "<op>", value: <val> } Nested group: { type: "nested", conjunction: "and"|"or", filterSet: [...] } Clear filters: { filterSet: [], conjunction: "and" } (or pass filters: null) Filter IDs (flt-prefixed) are auto-generated — do NOT include them. OPERATORS by field type — verified against Airtable's internal API (2026-04-17 capture; user report 2026-04-30): Text / URL / Email / Phone: "=" (exact match — value: string) "!=" (not equal) "contains" (value: string) "doesNotContain" "isEmpty" / "isNotEmpty" — input-side; auto-rewritten to "=" / "!=" "" before sending (the internal API rejects them on text fields with FAILED_STATE_CHECK) Number / Percent / Currency: "=", "!=", "<", ">", "<=", ">=", "isEmpty", "isNotEmpty" Single select: "=" (value: "selXXX" — the choice ID, NOT the choice name) "!=" "isAnyOf" / "isNoneOf" (value: ["selXXX", "selYYY"] — array of choice IDs) "isEmpty" / "isNotEmpty" Multiple select: "hasAnyOf", "hasAllOf", "hasNoneOf", "isExactly", "isEmpty", "isNotEmpty" Checkbox: "=" (value: true|false) Date (absolute): "is", "isBefore", "isAfter", "isOnOrBefore", "isOnOrAfter", "isEmpty", "isNotEmpty" value: ISO date string e.g. "2026-01-15" Date (relative) — "isWithin": value: { "mode": "<mode>", "timeZone": "<tz>", "shouldUseCorrectTimeZoneForFormulaicColumn": true } timeZone: IANA string e.g. "Europe/Istanbul", "America/New_York", "UTC" Modes (no numberOfDays): "pastWeek", "pastMonth", "pastYear", "nextWeek", "nextMonth", "nextYear", "thisCalendarMonth", "thisCalendarYear" Modes (add numberOfDays key): "pastNumberOfDays", "nextNumberOfDays" Example — past week: { "operator": "isWithin", "value": { "mode": "pastWeek", "timeZone": "UTC", "shouldUseCorrectTimeZoneForFormulaicColumn": true } } Example — past N days: { "operator": "isWithin", "value": { "mode": "pastNumberOfDays", "numberOfDays": 7, "timeZone": "UTC", "shouldUseCorrectTimeZoneForFormulaicColumn": true } } Example — this month: { "operator": "isWithin", "value": { "mode": "thisCalendarMonth", "timeZone": "UTC", "shouldUseCorrectTimeZoneForFormulaicColumn": true } } Formula / Lookup / Rollup (text result type): Same as Text. "isEmpty" / "isNotEmpty" are auto-rewritten to "=" / "!=" "". Linked record (foreignKey): "contains" (value: linked record name) works. "isEmpty" / "isNotEmpty" do NOT work — the call throws a clear error directing you to a helper formula like `IF(LEN({Linked} & "")>0,"yes","")` and a "=" / "!=" filter on that helper. AUTO-NORMALIZATION (applied client-side before the request): - "is" → "=" (the internal API does not recognize "is") - "isNot" → "!=" - "isAnyOf" with a single-element array or scalar value → "=" with scalar value - "isEmpty" → "=" "" on text / formula(text) / lookup(text) / rollup(text) fields - "isNotEmpty" → "!=" "" on text / formula(text) / lookup(text) / rollup(text) fields For single-select, value must be the choice ID (selXXX) — use get_base_schema to find IDs. NESTING LIMIT: The internal API accepts at most 2 levels of nesting (top conjunction + one layer of nested groups). Deeper trees are rejected with FAILED_STATE_CHECK. Workaround: flatten by repeating shared conditions inside each leaf group, e.g. `(A AND B) OR (A AND C)` instead of `A AND (B OR C)` if you need another nested AND inside the OR. The error message returned by this tool flags depth-related failures explicitly. EXAMPLES: Text equals: { filterSet: [{ columnId: "fldXXX", operator: "=", value: "Prime" }], conjunction: "and" } SingleSelect equals: { filterSet: [{ columnId: "fldXX…

reorder_view_fields

Reorder the fields (columns) displayed in a view. Accepts a partial map: pass only the field IDs you want to move, e.g. `{ "fldX": 1 }` to move fldX to position 1. Other fields keep their relative order. Index 0 is the leftmost position after the primary field. Internally the tool reads the view's current columnOrder, applies the moves, and sends the complete map (the underlying internal API rejects single-key inputs with FAILED_STATE_CHECK — user report 2026-04-30 §2.6).

show_or_hide_view_columns

Show or hide specific columns in a view without affecting others. Pass field IDs + a visibility flag — every listed ID is set to that state, all other columns are untouched. Use `set_view_columns` instead when you want to define the full visible set from scratch. Use `show_or_hide_all_columns` to bulk-toggle every column at once.

apply_view_sorts

Apply sort conditions to a view. Default mode replaces all existing sorts — pass an empty array with operation="replace" to clear. Use operation="append" to add new sorts on top of the view's existing sort stack without rewriting them.

update_view_group_levels

Set grouping on a view. Default mode replaces all existing group levels — pass an empty array with operation="replace" to clear grouping. Use operation="append" to add new group levels below the existing ones without rewriting them.

update_view_row_height

Change the row height of a grid view.

list_view_sections

List all sidebar sections for a table. Sections are user-organized groupings of views in the Airtable left sidebar (e.g. "🚀 Posting workflow", "🗑️ Sold workflow"). Returns each section's id, name, and the views inside it. The table-level `tableViewOrder` is a mixed list of view IDs and section IDs at the top level — when a view is inside a section, it appears in that section's `viewOrder`, NOT in the table's.

create_view_section

Create a new sidebar section in a table. Returns the new section ID (vsc-prefixed). Use `move_view_to_section` to populate it with views.

rename_view_section

Rename a sidebar section.

delete_view_section

Delete a sidebar section. Views inside the section are NOT deleted — Airtable auto-promotes them to ungrouped at the table-level position the section used to occupy. Verified 2026-04-30.

move_view_to_section

Move a view (or a section itself) within the sidebar. The single endpoint covers four user actions depending on the arguments: - viewId + sectionId → put the view INTO that section at targetIndex - viewId + sectionId: null → move the view OUT to ungrouped at table-level targetIndex - sectionId-as-viewIdOrSectionId + targetIndex → reorder the section among other sections - viewId + same section → reorder the view within its current section For section reorders, targetIndex is into the table's top-level mixed viewOrder; for in-section moves, it's into that section's viewOrder.

set_view_columns

One-shot view-column reset: hides every column then shows only `visibleColumnIds` in the given left-to-right order, with optional freeze. Use this for fresh view setup or full layout rewrites. Use `show_or_hide_view_columns` when you only want to toggle specific columns without touching the rest.

show_or_hide_all_columns

Show or hide every column in a view in one call. Use when you want a clean all-visible or all-hidden baseline. Use `set_view_columns` when you want to show a specific subset (it hides all then shows only the listed IDs). Use `show_or_hide_view_columns` for selective per-column toggles.

move_visible_columns

Move columns by visible-only index (index 0 = leftmost shown column, hidden columns not counted). Use when you want to position relative to what the user sees. Use `move_overall_columns` when you need to position relative to the full underlying column order including hidden fields. ⚠️ The API preserves existing relative order of supplied IDs — to place columns in a custom sequence, issue one call per column with incrementing targets.

move_overall_columns

Move one or more columns to a new position in the *overall* index (visible + hidden). Sibling of `move_visible_columns`. Index 0 is the leftmost column in the underlying full order.

update_frozen_column_count

Set the frozen-column divider position for a grid view. The first N columns from the left are frozen and stay visible during horizontal scroll.

set_view_cover

Set the cover-image field and crop/fit mode for Kanban or Gallery views. Pass `coverColumnId: null` to remove the cover. Either field can be passed independently — the other is left untouched.

set_view_color_config

Apply a color config to a view (Kanban / Gallery / Calendar). Currently supports `type: "selectColumn"` — card colors are taken from a single-select field's choice colors. Other types (e.g. rule-based coloring) exist in Airtable's UI but their payload shapes have not been fully captured yet — passing an unknown type is forwarded as-is so callers can experiment.

set_view_cell_wrap

Toggle whether long cell values wrap (multi-line) or truncate (single-line with ellipsis).

set_calendar_date_columns

Set the date-column ranges shown on a Calendar view. Each entry is either { startColumnId } for single-point events or { startColumnId, endColumnId } for range events. The array form lets a single calendar overlay multiple date series at once (e.g. "Created date" + "Start → End range" together).

set_form_metadata

Update one or more legacy-form-view metadata properties in a single call. Unset properties are not touched. Each property fans out to its own atomic Airtable endpoint. Supported properties: description — intro text shown above the form afterSubmitMessage — "thank you" text after submission redirectUrl — URL to redirect to after submit refreshAfterSubmit — post-submit behavior (e.g. "REFRESH_BUTTON") shouldAllowRequestCopyOfResponse — boolean: show "send me a copy" toggle to respondents shouldAttributeResponses — boolean: track which user submitted (for signed-in respondents) isAirtableBrandingRemoved — boolean: hide Airtable branding (paid plans only) Note: "form title" is the view name itself — use rename_view to change it. "Field labels on the form" use a per-field endpoint that has not been captured yet.

set_form_submission_notification

Toggle email-on-submit notifications for a specific user on a form view. Per-user, not per-form (separate from set_form_metadata).

update_field_description

Update the description text of a field.

duplicate_field

Duplicate (clone) a field in a table. Optionally also duplicate the cell values.

create_extension

Create a new extension (block) in an Airtable base. Returns the block ID needed for installation. Use this to register custom extensions before installing them.

create_extension_dashboard

Create a new extension dashboard page in a base. Extensions are installed onto dashboard pages.

install_extension

Install an extension onto a dashboard page. Requires a block ID (from create_extension) and a page ID (from create_extension_dashboard).

update_extension_state

Enable or disable an extension installation.

rename_extension

Rename an installed extension.

duplicate_extension

Duplicate an installed extension on a dashboard page.

remove_extension

Remove an installed extension from a dashboard.

query_records

Read records from an Airtable table view. Returns resolved field values including lookup fields. Supports optional client-side text search across all field values — unlike the REST API filterByFormula approach, this search works correctly on lookup fields. Fetch up to 1000 records per call.

duplicate_records

Duplicate one or more existing records within a table. Creates exact copies of the specified source records in the same table and view. Returns the new record IDs.

create_records

Create one or more records in a table. Each item supplies cellValuesByColumnId (computed fields are read-only and must be omitted). Returns created record IDs. Per-row isolation: a failing row is reported, not fatal.

update_records

Update primitive / single-select cells of existing records via cellValuesByColumnId. (Array cells — multi-select, links, attachments — are not set here.) Per-row isolation.

upload_attachment

Upload attachments into an attachment cell by URL. Airtable's servers fetch each URL directly (the UI's 'Add attachment → Add URL') — bytes are NOT proxied through this server. Use for multipleAttachments fields, which update_records cannot set. Appends to the cell (calling twice adds two). Per-update isolation: a failing update is reported, not fatal.

delete_records

Delete one or more records from a table in a single batch call. The returned deleted count equals rowIds.length (optimistic) — already-deleted rows are silently skipped by the server.

sync_base

Base-to-base schema + record sync. IMPORTANT: BOTH mode="plan" and mode="apply" run as BACKGROUND JOBS — they return {jobId, planId, status:"running"} IMMEDIATELY (jobId === planId), NOT a synchronous result; poll mode="status" with that planId to get progress and the final result. mode="plan" (read-only, does NOT mutate): computes an ordered plan (tables/fields to create/update + orphans + warnings) by comparing source/dest schema; when the job finishes, mode="status" returns the plan digest in planDigest. mode="apply" (requires planId from a prior plan): executes the saved plan against the destination — creates tables, reconciles the primary, creates scalar/link/computed fields (source->dest reference remapping + formula validation), applies non-destructive field updates, then runs the RECORD sync (two-pass cells + links, attachments, view-filter restore); aborts if the destination drifted since the plan. mode="status" (poll a plan OR apply job by its planId): returns { phase: "planning"|"schema"|"records"|"done"|"failed", status, recordsMapped, summary, schemaResult?, recordsResult?, result?, planDigest? }. planDigest is human-only by default ({ human, machineOmitted: true }) — pass verbose:true for the full planDigest.machine. Field-mapping errors, APPLY_LOCKED, and DRIFT surface HERE (phase="failed" or an aborted schemaResult), not as a synchronous error. mode="reconcile" (SYNCHRONOUS): rebuild/repair the record map — existence-prune dead idmap entries, optional natural-key re-match per table. mode="diff" (SYNCHRONOUS): schema digest comparing source and destination WITHOUT saving a plan; returns a diffId and summary; pass detail=<section> to drill into a section of a prior diff.

manage_daemon

Inspect and control the MCP daemon this server runs in. action="status" is the diagnostic to reach for FIRST when tools start failing: it reports whether a daemon is running and whether YOU are it, the transport, uptime, version/provenance, the tunnel URL, and — the part nothing else exposes — the live session state (sessionDead, the last circuit-breaker trip INCLUDING Airtable's own response body, and the browser/auth busy queue). That is how you tell "daemon gone" from "session dead" from "browser busy" instead of guessing. Control actions: start (idempotent; attaches to a healthy daemon rather than starting a second one), restart, stop (writes a sentinel so the VS Code extension does not silently respawn it), tunnel_enable, tunnel_disable, token_rotate. stop/restart answer first and exit afterwards, so this call returns normally and the daemon goes away a moment later. token_rotate and tunnel_* are loopback-only and are refused for callers arriving through the tunnel; status is returned to them with host-identifying fields blanked. The bearer token is never returned to anyone. Interactive tunnel setup (cloudflared login, creating a named tunnel) is deliberately NOT here — use the CLI or the VS Code dashboard.

manage_tools

Control which tools are available. Actions: list_profiles, switch_profile, get_tool_status, toggle_tool, toggle_category. Use this to switch between read-only, safe-write, full, or custom profiles, or enable/disable individual tools. Active profile: "full" — all tools enabled.

Claude Desktop / Cursor

Paste into your MCP client config file to install this server.

{
    "mcpServers": {
        "airtable mcp server": {
            "airtable": {
                "command": "npx",
                "args": [
                    "-y",
                    "airtable-user-mcp"
                ]
            }
        }
    }
}

McpServers

{
    "airtable": {
        "command": "npx",
        "args": [
            "-y",
            "airtable-user-mcp"
        ]
    }
}

What airtable-user-mcp adds on top of the official Airtable MCP

This is a coverage map, not a "pick one" decision — the two servers are complementary and designed to run side-by-side.

| Capability | Official Airtable MCP | airtable-user-mcp |
|---|---|---|
| Total tools | ~17 | 66 |
| Auth model | Personal Access Token or OAuth, per-scope setup | Log in once with your normal Airtable account (SSO/2FA supported) |
| Transport | HTTP (remote) | stdio (local, private) |
| Data never leaves your machine | ❌ Requests go through mcp.airtable.com | ✅ Runs locally against Airtable's API |
| Schema read (bases, tables, fields, views) | Partial (no view config) | Full — filters, sorts, groups, visibility, row height, descriptions |
| Read records with resolved field values | Partial | ✅ query_records — returns lookup/rollup/formula fields fully resolved |
| Search records by text (incl. lookup fields) | ❌ filterByFormula with FIND()/SEARCH() silently fails on lookup fields | ✅ query_records.search — substring match on all resolved values |
| Duplicate records | ❌ | ✅ duplicate_records |
| Create formula fields | ❌ UNSUPPORTED_FIELD_TYPE_FOR_CREATE | ✅ |
| Create rollup fields | ❌ | ✅ |
| Create lookup / multipleLookupValues fields | ❌ | ✅ |
| Create count fields | ❌ | ✅ |
| Update formula text of an existing field | ❌ | ✅ |
| Validate a formula before applying | ❌ | ✅ |
| Rename / duplicate / safely delete fields | Partial (no duplicate, no dependency summary) | ✅ with expectedName guard + dependency preview |
| Create views (grid/form/kanban/calendar/gallery/gantt/list) | ❌ (API has no create-view endpoint) | ✅ |
| Set/append view filters (nested AND/OR) | ❌ | ✅ |
| Set view sorts | ❌ | ✅ |
| Set view grouping | ❌ | ✅ |
| Change column order | ❌ | ✅ |
| Show/hide columns in a view | ❌ | ✅ |
| Change row height | ❌ | ✅ |
| Duplicate a view with its full configuration | ❌ | ✅ |
| View descriptions, cell wrap, covers, color config, calendar dates, frozen columns | ❌ | ✅ |
| Sidebar sections (create, rename, move, delete) | ❌ | ✅ |
| Record templates (create, pre-fill, duplicate, apply, delete) | ❌ | ✅ |
| Form metadata (description, redirect, attribution, branding) | ❌ | ✅ |
| Extension / block management (install, enable, rename, duplicate, remove) | ❌ | ✅ |
| Create dashboard pages | ❌ | ✅ |
| Tool profiles & per-tool toggles | ❌ | ✅ read-only / safe-write / full / custom |
| Destructive-action safety guards | Relies on token scopes | ✅ expectedName match, dependency summary, force flag |
| Batch record create limit | 10 / request | Uses the same Airtable limit; no added restriction |
| VS Code / Cursor / Windsurf / Cline / Amp one-click install | Manual JSON edit per IDE | ✅ One click via the companion extension |
| Formula editor with IntelliSense | ❌ | ✅ (VS Code extension) |
| Credentials storage | You manage the PAT | OS keychain, auto-refresh |
| Plan requirement | Airtable plan with API access + token scopes | Any plan you can log into |
| Price | Free | Free, MIT |

Sources: Airtable's official MCP docs, Airtable Web API reference, and the UNSUPPORTED_FIELD_TYPE_FOR_CREATE rollup thread.

---

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.