·

Airtable MCP With OpenCode

Set up Airtable MCP in OpenCode so your AI agent can read and update bases and records right from your editor.

OpenCode's MCP support is solid but younger and less battle-tested than Claude Code's — the config format is simpler, the permission model is coarser, and a few Airtable-specific rough edges show up once you push past basic reads. This topic covers getting the server wired into OpenCode, the read/write patterns that work well, a full feature-matrix build example, and the specific limitations you should plan around rather than discover mid-workflow.

Installing and Connecting Airtable MCP to OpenCode

OpenCode reads MCP server definitions from opencode.json (project-level) or ~/.config/opencode/opencode.json (global). Add Airtable MCP at the project level:

{
  "mcp": {
    "airtable": {
      "type": "local",
      "command": ["npx", "-y", "airtable-mcp-server"],
      "environment": {
        "AIRTABLE_API_KEY": "{env:AIRTABLE_API_KEY}"
      }
    }
  }
}

OpenCode's {env:VAR_NAME} interpolation syntax differs from Claude Code's ${VAR} — copying a config verbatim between the two tools is a common source of "server won't start" confusion. Export the token in your shell before launching:

export AIRTABLE_API_KEY="patAbC123dEf456.gh789ij012kl345mn678op901qr234st567uv890wx123yz456ab789cd012"
opencode

Confirm the server loaded with OpenCode's session MCP inspector:

/mcp

You should see airtable listed as connected with its tool count. If the process fails silently, run the underlying command directly to isolate whether it's an OpenCode config issue or an Airtable auth issue:

AIRTABLE_API_KEY=pat... npx -y airtable-mcp-server

If that hangs waiting on stdio without erroring, the server itself is fine and the problem is in how OpenCode is invoking it — usually a malformed command array (OpenCode expects the array form, not a single shell string, for type: local servers).

OpenCode also supports type: remote for MCP servers exposed over HTTP/SSE, which matters if your org runs a centrally-hosted Airtable MCP gateway (some teams do this to avoid distributing raw PATs to every developer machine, instead putting the PAT behind an internal auth-proxied endpoint):

{
  "mcp": {
    "airtable": {
      "type": "remote",
      "url": "https://mcp-gateway.internal.example.com/airtable",
      "headers": {
        "Authorization": "Bearer {env:INTERNAL_GATEWAY_TOKEN}"
      }
    }
  }
}

Tips
- Double check {env:VAR} vs ${VAR} syntax when porting a config from Claude Code docs or examples — it's the single most common copy-paste error moving between these two clients.
- Test the raw npx airtable-mcp-server command standalone before troubleshooting inside OpenCode — it isolates config problems from server problems in seconds.
- If your org already proxies Airtable access, prefer the remote type over distributing raw PATs to every contributor's local opencode.json.


Reading Tables and Writing Records from OpenCode

OpenCode's agent loop is more literal than Claude Code's — it tends to execute closer to what you type rather than inferring intent, so specificity in prompts pays off more here. For a read:

Use the airtable tool to list tables in base appXXXXXXXXXXXXXX. Then run
list_records on the "Features" table with filterByFormula
{Status} = "In Development" and show Title, Owner, Target Release.

Spelling out the base ID (appXXXXXXXXXXXXXX) rather than a name matters more in OpenCode — some earlier server versions don't cache a resolved base name to ID mapping across tool calls the way Claude Code's context handling smooths over, so being explicit avoids a redundant list_bases round trip every single prompt.

For writes, the same explicitness applies. A record creation prompt:

Create a record in the "Features" table of base appXXXXXXXXXXXXXX with fields:
Title = "Bulk CSV export", Status = "Backlog", Owner = "jane@example.com",
Target Release = "2026-Q4".

Resulting payload:

{
  "fields": {
    "Title": "Bulk CSV export",
    "Status": "Backlog",
    "Owner": "jane@example.com",
    "Target Release": "2026-Q4"
  }
}

Note: if Owner is actually a "Collaborator" field type (Airtable's user-reference field, distinct from a plain text field), passing an email string directly fails — collaborator fields expect an object shape:

{
  "fields": {
    "Owner": { "email": "jane@example.com" }
  }
}

This is a genuinely easy mistake to make because the two field types render identically in the Airtable UI (a name/email chip) but have completely different write schemas underneath. Always run describe_table first in a new base — OpenCode won't proactively warn you about this mismatch the way a stricter typed client might; the tool call will just fail with a 422 and a somewhat generic error message.

Updating records follows the same ID-first pattern as every other client — resolve the record ID via a read, then write:

Find the record where {Title} = "Bulk CSV export" in Features, then update
its Status to "In Development".

Tips
- Pass base IDs explicitly in OpenCode prompts rather than relying on a name being remembered from earlier in the session — it's cheaper and more reliable than an implicit lookup.
- Always confirm field type (text vs. collaborator vs. link) via describe_table before writing to a field you haven't touched yet in this session — OpenCode's error messages on type mismatches are functional but not verbose.
- Keep write prompts to single, explicit field-value pairs rather than natural-language paragraphs when working in OpenCode; it parses literal instructions more reliably than inferred structure.


Practical Example: Building a Feature Matrix from Product Requirements

A feature matrix — rows of features, columns of plan tiers (Free/Pro/Enterprise) with checkmarks or notes — is one of the more mechanical, high-value things to hand an agent, because it's fundamentally a reshape of existing requirement data into a presentation table.

Starting state: a Requirements table with Title, Plan Availability (multi-select: Free, Pro, Enterprise), Status. Target: a Feature Matrix table with one row per shipped feature and boolean columns per plan tier.

Read all records from Requirements where {Status} = "Shipped".
For each one, create a record in Feature Matrix with:
- Feature Name = Title
- Free = true if "Free" is in Plan Availability, else false
- Pro = true if "Pro" is in Plan Availability, else false
- Enterprise = true if "Enterprise" is in Plan Availability, else false
Skip any record that already has a matching Feature Name in Feature Matrix
to avoid duplicates.

The dedup instruction matters — without it, re-running this prompt after shipping five more features re-creates every existing row instead of just adding the new ones. A safer version pre-fetches existing matrix entries first:

Step 1: List all existing Feature Name values in Feature Matrix.
Step 2: List all Shipped requirements from Requirements NOT already in that list.
Step 3: Show me the diff (which features will be added) before creating anything.
Step 4: On confirmation, create the new records.

Resulting create payload for one feature:

{
  "fields": {
    "Feature Name": "Bulk CSV export",
    "Free": false,
    "Pro": true,
    "Enterprise": true
  }
}

For a more advanced version, generate a filterByFormula-driven summary directly rather than materializing every row — useful when you want a quick markdown table for a stakeholder update without touching the base at all:

Query Requirements where Status = "Shipped", group by Plan Availability values,
and render a markdown table: rows = feature name, columns = Free/Pro/Enterprise,
cell = checkmark or blank. Don't write anything to Airtable, just output the table.

That last instruction — "don't write anything" — is worth using liberally whenever you want the agent to reason over Airtable data for a one-off report rather than persist a materialized copy. It's easy to forget the agent defaults toward taking action when it has write tools available.

Tips
- Always run a dedup check (existing values vs. incoming) before any "build a matrix from requirements" job you expect to re-run periodically — otherwise duplicate rows accumulate silently.
- Use the "don't write anything, just show me" instruction explicitly for any exploratory or reporting task — an agent with write tools available will sometimes act rather than just report unless told not to.
- Keep the source-of-truth multi-select field (Plan Availability) as the single place tier assignment is edited, and treat the Feature Matrix table as a derived view an agent regenerates — don't let humans hand-edit both, they will drift apart within a sprint.


Known Limitations for Airtable MCP in OpenCode

A few limitations are specific to the OpenCode + Airtable MCP combination rather than Airtable MCP in general, as of the current OpenCode release cycle (mid-2026):

  • No persistent tool-call context caching across turns the way Claude Code does. Every prompt referencing a base or table benefits from re-stating the ID explicitly; relying on "the base we were just looking at" works less reliably than in Claude Code, especially in longer sessions.
  • Attachment field writes are unreliable. Passing {"url": "https://..."} for attachment fields sometimes silently drops the attachment if the source URL requires authentication headers Airtable's fetcher can't supply — this is an Airtable-API-level constraint, not OpenCode's fault, but OpenCode surfaces no warning when it happens; you have to verify the attachment landed with a follow-up read.
  • Schema-mutation tools (create_field, update_table) are present in the server but produce inconsistent results when invoked through OpenCode's tool-calling loop — a field created via the raw API works fine, but the same operation prompted through OpenCode occasionally omits field options (e.g. a new single-select created without its choices populated). Verify any schema change immediately with describe_table.
  • No built-in rate-limit backoff surfaced to the user. When OpenCode hits a 429 from Airtable, it reports the raw error rather than automatically retrying with the Retry-After delay — you need to explicitly instruct "if you get a 429, wait 30 seconds and retry" in prompts for bulk jobs, or wrap the operation in your own retry logic outside the agent.
  • Remote MCP (type: remote) support for Airtable specifically is less tested than local stdio — if you're running through an internal gateway, budget extra time for connection debugging versus the straightforward npx local setup.

None of these are dealbreakers for day-to-day requirements tracking, but they do mean OpenCode sessions benefit from more explicit, more verbose prompts than the equivalent Claude Code workflow — the convenience of implicit context carries over less.

Tips
- Verify any AI-driven schema change (new field, new select option) immediately with a describe_table call — don't trust that options/choices came through correctly on the first try.
- Explicitly instruct retry-on-429 behavior for any bulk job; OpenCode won't do it automatically the way some other clients' server wrappers do.
- Re-verify attachment field writes with a follow-up read rather than trusting the create call succeeded — silent drops on authenticated source URLs are a known gap.


Tips

Tips
- Be more explicit and more verbose in OpenCode prompts than you would in Claude Code — state base IDs, field types, and desired dedup/retry behavior directly rather than relying on inferred context.
- Run describe_table at the start of every new base session before any write — it's cheap and it's the single best defense against the collaborator-field and formula-field write failures that show up most often.
- For recurring jobs (feature matrix rebuilds, status syncs), write the full multi-step prompt (preview → diff → confirm → execute) into a saved OpenCode command or snippet file rather than retyping it — consistency matters more than terseness here.