MCP server for managing Airtable bases, tables, records, and views. Perform CRUD operations, search with formulas, and manage your Airtable data programmatically.
- Base Management: List and explore accessible bases
- Schema Access: Get table structures and field definitions
- Record Operations: Create, read, update, and delete records
- Advanced Search: Filter records using Airtable formulas
- Bulk Operations: Create or update up to 10 records at once
- View Management: List and work with table views
- Pagination: Handle large datasets with offset-based pagination
- Flexible Filtering: Sort and filter records with powerful queries
- Airtable account
- Access token with appropriate scopes
AIRTABLE_ACCESS_TOKEN
(required): Your Airtable personal access token
How to create an access token:
- Go to airtable.com/create/tokens
- Click "Create new token"
- Give your token a name (e.g., "MCP Server Access")
- Add the required scopes:
data.records:read
- Read recordsdata.records:write
- Create, update, delete recordsschema.bases:read
- Read base schemas
- Add access to specific bases or select "All current and future bases"
- Click "Create token" and copy it immediately
- Store it securely as
AIRTABLE_ACCESS_TOKEN
List all accessible Airtable bases.
Example:
bases = await list_bases()
Response includes:
- Base IDs
- Base names
- Permission levels
Get complete schema for a base including tables and fields.
Parameters:
base_id
(string, required): Base ID (e.g., 'appXXXXXXXXXXXXXX')
Example:
schema = await get_base_schema(base_id="appAbc123")
Response includes:
- Table IDs and names
- Field definitions with types
- View information
List records from a table with advanced filtering and sorting.
Parameters:
base_id
(string, required): Base IDtable_id_or_name
(string, required): Table ID or namefields
(list, optional): Specific fields to returnfilter_by_formula
(string, optional): Airtable formula filtermax_records
(int, optional): Maximum records to returnpage_size
(int, optional): Records per page (max: 100)sort
(list, optional): Sort configurationview
(string, optional): View name or IDoffset
(string, optional): Pagination offset
Example:
# List all records
records = await list_records(
base_id="appAbc123",
table_id_or_name="Contacts"
)
# List with filters and sorting
records = await list_records(
base_id="appAbc123",
table_id_or_name="Contacts",
fields=["Name", "Email", "Status"],
filter_by_formula="{Status} = 'Active'",
sort=[{"field": "Name", "direction": "asc"}],
max_records=50
)
# Pagination
page1 = await list_records(
base_id="appAbc123",
table_id_or_name="Contacts",
page_size=100
)
# Use offset from page1 response for next page
page2 = await list_records(
base_id="appAbc123",
table_id_or_name="Contacts",
page_size=100,
offset=page1["offset"]
)
Search records using Airtable formula filters.
Parameters:
base_id
(string, required): Base IDtable_id_or_name
(string, required): Table ID or nameformula
(string, required): Airtable formulafields
(list, optional): Fields to returnsort
(list, optional): Sort configurationmax_records
(int, optional): Maximum records
Example:
# Search with complex formula
results = await search_records(
base_id="appAbc123",
table_id_or_name="Contacts",
formula="AND({Status} = 'Active', {Age} > 25, FIND('gmail', {Email}))",
fields=["Name", "Email", "Age"],
sort=[{"field": "Age", "direction": "desc"}],
max_records=20
)
Get a specific record by ID.
Parameters:
base_id
(string, required): Base IDtable_id_or_name
(string, required): Table ID or namerecord_id
(string, required): Record ID (e.g., 'recXXXXXXXXXXXXXX')
Example:
record = await get_record(
base_id="appAbc123",
table_id_or_name="Contacts",
record_id="recDef456"
)
Create a new record in a table.
Parameters:
base_id
(string, required): Base IDtable_id_or_name
(string, required): Table ID or namefields
(dict, required): Field names and values
Example:
new_record = await create_record(
base_id="appAbc123",
table_id_or_name="Contacts",
fields={
"Name": "John Doe",
"Email": "john@example.com",
"Age": 30,
"Status": "Active"
}
)
Update an existing record.
Parameters:
base_id
(string, required): Base IDtable_id_or_name
(string, required): Table ID or namerecord_id
(string, required): Record IDfields
(dict, required): Fields to updatereplace_all
(bool, optional): If True, replace all fields (PUT). If False, merge (PATCH). Default: False
Example:
# Partial update (merge fields)
updated = await update_record(
base_id="appAbc123",
table_id_or_name="Contacts",
record_id="recDef456",
fields={
"Status": "Inactive",
"Notes": "Updated status"
},
replace_all=False
)
# Complete replacement
replaced = await update_record(
base_id="appAbc123",
table_id_or_name="Contacts",
record_id="recDef456",
fields={
"Name": "Jane Smith",
"Email": "jane@example.com",
"Age": 28
},
replace_all=True
)
Delete a record from a table.
Parameters:
base_id
(string, required): Base IDtable_id_or_name
(string, required): Table ID or namerecord_id
(string, required): Record ID
Example:
result = await delete_record(
base_id="appAbc123",
table_id_or_name="Contacts",
record_id="recDef456"
)
Create multiple records at once (up to 10 per request).
Parameters:
base_id
(string, required): Base IDtable_id_or_name
(string, required): Table ID or namerecords
(list, required): List of record objects with fields (max: 10)
Example:
results = await bulk_create_records(
base_id="appAbc123",
table_id_or_name="Contacts",
records=[
{"Name": "Alice", "Email": "alice@example.com", "Age": 25},
{"Name": "Bob", "Email": "bob@example.com", "Age": 30},
{"Name": "Charlie", "Email": "charlie@example.com", "Age": 35}
]
)
Update multiple records at once (up to 10 per request).
Parameters:
base_id
(string, required): Base IDtable_id_or_name
(string, required): Table ID or namerecords
(list, required): List with 'id' and 'fields' (max: 10)replace_all
(bool, optional): Replace all fields or merge. Default: False
Example:
results = await bulk_update_records(
base_id="appAbc123",
table_id_or_name="Contacts",
records=[
{
"id": "recDef456",
"fields": {"Status": "Active"}
},
{
"id": "recGhi789",
"fields": {"Status": "Inactive"}
}
],
replace_all=False
)
Get field definitions for a specific table.
Parameters:
base_id
(string, required): Base IDtable_id_or_name
(string, required): Table ID or name
Example:
fields = await get_table_fields(
base_id="appAbc123",
table_id_or_name="Contacts"
)
Response includes:
- Field IDs
- Field names
- Field types
- Field options
List all views in a table.
Parameters:
base_id
(string, required): Base IDtable_id_or_name
(string, required): Table ID or name
Example:
views = await list_views(
base_id="appAbc123",
table_id_or_name="Contacts"
)
Response includes:
- View IDs
- View names
- View types (grid, form, calendar, etc.)
Airtable uses a formula language similar to Excel for filtering and calculations.
- Comparison:
=
,!=
,>
,<
,>=
,<=
- Logical:
AND()
,OR()
,NOT()
- Text:
FIND()
,SEARCH()
,LEFT()
,RIGHT()
,MID()
,LEN()
- Math:
+
,-
,*
,/
,MOD()
,ROUND()
- Date:
TODAY()
,NOW()
,DATEADD()
,DATEDIF()
,IS_AFTER()
,IS_BEFORE()
Filter by single condition:
"{Status} = 'Active'"
Filter by multiple conditions:
"AND({Status} = 'Active', {Age} > 25)"
Filter with OR:
"OR({Status} = 'Active', {Status} = 'Pending')"
Text search:
"FIND('gmail', {Email})" # Contains gmail
Date filters:
"IS_AFTER({Created}, '2024-01-01')"
Complex formula:
"AND(
OR({Status} = 'Active', {Status} = 'Pending'),
{Age} >= 18,
FIND('@company.com', {Email})
)"
Empty/not empty:
"{Email} != ''" # Email is not empty
"{Phone} = ''" # Phone is empty
Sort by one or more fields:
# Single field
sort=[{"field": "Name", "direction": "asc"}]
# Multiple fields
sort=[
{"field": "Status", "direction": "desc"},
{"field": "Name", "direction": "asc"}
]
Directions: asc
(ascending) or desc
(descending)
Airtable supports various field types:
- Single line text: Short text strings
- Long text: Multi-line text
- Number: Integers or decimals
- Checkbox: Boolean true/false
- Date: Date values
- Phone number: Phone numbers
- Email: Email addresses
- URL: Website URLs
- Single select: Choose one option
- Multiple select: Choose multiple options
- Collaborator: Airtable users
- Attachment: Files and images
- Link to another record: Relationships
- Lookup: Values from linked records
- Rollup: Aggregations from linked records
- Formula: Calculated values
- Count: Count of linked records
- Rating: Star ratings
- Barcode: Barcode scanner
- Button: Action buttons
Airtable returns up to 100 records per request. Use pagination for more:
all_records = []
offset = None
while True:
response = await list_records(
base_id="appAbc123",
table_id_or_name="Contacts",
page_size=100,
offset=offset
)
all_records.extend(response["records"])
# Check if there are more pages
if "offset" in response:
offset = response["offset"]
else:
break # No more records
Airtable enforces rate limits:
- 5 requests per second per base
- Applies to all API operations on a specific base
- Exceeding limits returns HTTP 429 error
Best practices:
- Implement exponential backoff for retries
- Use bulk operations when possible (10 records per request)
- Cache frequently accessed data
- Batch multiple operations together
- Format:
app
+ 14 alphanumeric characters - Example:
appAbc123Def456Gh
- Can use table ID or URL-encoded table name
- Table ID format:
tbl
+ alphanumeric - Example:
tblXyz789
orContacts
- Format:
rec
+ 14 alphanumeric characters - Example:
recDef456Ghi789Jk
- Format:
viw
+ alphanumeric - Example:
viwMno012Pqr345St
Upload attachments as URLs:
await create_record(
base_id="appAbc123",
table_id_or_name="Documents",
fields={
"Name": "My Document",
"Files": [
{"url": "https://example.com/file1.pdf"},
{"url": "https://example.com/image.jpg"}
]
}
)
Common error codes:
- 401 Unauthorized: Invalid or missing access token
- 403 Forbidden: Insufficient permissions
- 404 Not Found: Base, table, or record doesn't exist
- 422 Unprocessable: Invalid field values or formula syntax
- 429 Too Many Requests: Rate limit exceeded
- 503 Service Unavailable: Airtable service issue
Error response format:
{
"error": {
"type": "INVALID_REQUEST_UNKNOWN",
"message": "Error description"
}
}
- Use bulk operations: Create/update up to 10 records per request
- Cache base schemas: Schemas don't change frequently
- Filter on the server: Use formulas instead of filtering locally
- Limit returned fields: Request only needed fields
- Handle rate limits: Implement retry logic with backoff
- Use views: Leverage Airtable views for pre-filtered data
- Validate data: Check field types before creating/updating
- URL-encode table names: Use proper encoding for table names with spaces
- CRM Integration: Sync customer data with Airtable
- Project Management: Track tasks and milestones
- Content Management: Manage blog posts and media
- Inventory Tracking: Monitor stock levels and orders
- Event Management: Coordinate attendees and schedules
- Form Processing: Store and manage form submissions
- Data Migration: Import/export data programmatically
- Automation: Build workflows with Airtable as data store
For detailed information:
- Never commit tokens: Store access tokens securely
- Scope permissions: Grant minimum required access
- Rotate tokens: Regularly update access tokens
- Use HTTPS: All API calls use HTTPS
- Audit access: Review token usage regularly
- Revoke unused tokens: Delete tokens no longer needed