MCP Server
The @air/mcp package connects MCP-compatible AI clients to an Air workspace. It exposes tools for finding and previewing assets, organizing boards, applying tags and custom fields, importing files from URLs, and uploading local files.
The server supports:
- STDIO for clients that launch the package as a local child process. This is the recommended setup for desktop and coding clients.
- Streamable HTTP for local HTTP-capable clients and development.
Prerequisites
- Node.js 18 or newer
- An Air API key and workspace ID. See Getting Started for credential setup.
Configuration
The server reads these environment variables:
| Variable | Required | Description |
|---|---|---|
AIR_API_KEY | Yes | API key used to authenticate with Air |
AIR_WORKSPACE_ID | Yes | Workspace that all MCP tools operate on |
AIR_API_URL | No | API base URL override for non-production environments |
Credentials stay in the MCP server process. Do not place them in prompts or tool arguments.
Command-line options
| Option | Description |
|---|---|
--http | Use Streamable HTTP instead of STDIO |
--port <number> | Set the initial HTTP port. The default is 3000; if occupied, the server tries up to 10 subsequent ports. |
--debug | In HTTP mode, log parsed MCP requests and responses to stderr |
HTTP mode binds to 127.0.0.1 and serves MCP requests at /mcp.
Set up a client
Claude Desktop
Add the server to your Claude Desktop config. On macOS, the file is ~/Library/Application Support/Claude/claude_desktop_config.json.
{
"mcpServers": {
"air": {
"command": "npx",
"args": ["-y", "@air/mcp"],
"env": {
"AIR_API_KEY": "your-api-key",
"AIR_WORKSPACE_ID": "your-workspace-id"
}
}
}
}If Claude Desktop does not load the Node.js version from your shell profile, set command to the full path of a Node 18+ npx executable. See the Claude Desktop MCP guide.
Claude Code
claude mcp add \
--env AIR_API_KEY=your-api-key \
--env AIR_WORKSPACE_ID=your-workspace-id \
--transport stdio \
air \
-- npx -y @air/mcpSee the Claude Code MCP documentation.
Codex
Add the server to ~/.codex/config.toml:
[mcp_servers.air]
command = "npx"
args = ["-y", "@air/mcp"]
[mcp_servers.air.env]
AIR_API_KEY = "your-api-key"
AIR_WORKSPACE_ID = "your-workspace-id"Or use the CLI:
codex mcp add air \
--env AIR_API_KEY=your-api-key \
--env AIR_WORKSPACE_ID=your-workspace-id \
-- npx -y @air/mcpSee the Codex MCP documentation.
Cursor
Add the server to .cursor/mcp.json in a project or ~/.cursor/mcp.json globally:
{
"mcpServers": {
"air": {
"command": "npx",
"args": ["-y", "@air/mcp"],
"env": {
"AIR_API_KEY": "your-api-key",
"AIR_WORKSPACE_ID": "your-workspace-id"
}
}
}
}See the Cursor MCP documentation.
Windsurf
Add the server to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"air": {
"command": "npx",
"args": ["-y", "@air/mcp"],
"env": {
"AIR_API_KEY": "your-api-key",
"AIR_WORKSPACE_ID": "your-workspace-id"
}
}
}
}See the Windsurf MCP documentation.
VS Code with GitHub Copilot
Add the server to .vscode/mcp.json, or open the user-level file with MCP: Open User Configuration:
{
"servers": {
"air": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@air/mcp"],
"env": {
"AIR_API_KEY": "your-api-key",
"AIR_WORKSPACE_ID": "your-workspace-id"
}
}
}
}Use Copilot in Agent mode to call MCP tools. See the VS Code MCP documentation.
Zed
Add the server to ~/.config/zed/settings.json:
{
"context_servers": {
"air": {
"command": "npx",
"args": ["-y", "@air/mcp"],
"env": {
"AIR_API_KEY": "your-api-key",
"AIR_WORKSPACE_ID": "your-workspace-id"
}
}
}
}See the Zed context server documentation.
Run with Streamable HTTP
Start a local HTTP server with credentials in the process environment:
AIR_API_KEY=your-api-key \
AIR_WORKSPACE_ID=your-workspace-id \
npx -y @air/mcp --httpConnect a local HTTP-capable MCP client to:
http://localhost:3000/mcpFor example, add --port 4000 to choose another initial port or --debug to inspect MCP traffic.
Do not expose this endpoint directly to the internet. It binds to loopback and does not authenticate incoming MCP connections. Remote products such as ChatGPT custom apps require a remotely reachable MCP server; use a secure, authenticated deployment or proxy if adapting this package for remote access. See OpenAI's MCP documentation.
Tool reference
The server registers 23 tools in six categories. A parameter followed by ? is optional.
List tools return a JSON object with data, pagination, and total. To request another page, pass the returned pagination cursor into the next call.
Assets
| Tool | Inputs | Description |
|---|---|---|
list_assets | parentBoardId?, includeNestedAssets?, tag?, customField?, search?, limit?, cursor?, includeThumbnails? | List assets with board, tag, custom-field, and search filters. tag and customField accept one string or an array. includeThumbnails adds inline images for up to the first 10 results. |
list_nested_assets | boardId, limit?, cursor?, includeThumbnails? | List assets in a board and all nested sub-boards. Can include up to 10 inline thumbnails. |
get_asset | assetId | Get asset details, cover version, metadata, custom fields, and a canonical Air web app url. |
get_asset_download_url | assetId, versionId? | Get a temporary download URL. If versionId is omitted, the cover version is used. |
get_asset_boards | assetId, limit?, cursor? | List every board that contains an asset. |
get_asset_image | assetId, size? | Return an inline base64 image content block. size is thumbnail (default) or preview. |
rename_asset | assetId, title, versionId? | Update only the display title. Defaults to the cover version and does not change the underlying file name. |
update_asset | assetId, title?, description?, versionId? | Update the display title and/or description. Defaults to the cover version. |
add_tag_to_asset | assetId, tagId, versionId? | Add a tag to an asset version. Defaults to the cover version. |
remove_tag_from_asset | assetId, tagId, versionId? | Remove a tag from an asset version. Defaults to the cover version. |
update_asset_custom_field | assetId, customFieldId, value?, values? | Set or clear a custom field value. Field-specific rules are described below. |
Boards
| Tool | Inputs | Description |
|---|---|---|
list_boards | name?, parentBoardId?, limit?, cursor? | List boards, optionally filtered by name or parent. |
get_board | boardId | Get board details and a canonical Air web app url. |
create_board | title, description?, parentBoardId? | Create a root board or a board nested under another board. Returns a canonical url. |
update_board | boardId, title?, description?, parentBoardId? | Update a board. Pass parentBoardId: null to move it to the workspace root. |
add_asset_to_board | boardId, assetIds | Add one or more asset IDs to a board. |
Tags
| Tool | Inputs | Description |
|---|---|---|
list_tags | name?, limit?, cursor? | List tags, optionally filtered by name. |
create_tag | name | Create a tag in the workspace. |
Custom fields
| Tool | Inputs | Description |
|---|---|---|
list_custom_fields | limit?, cursor? | List custom fields defined in the workspace. |
get_custom_field | customFieldId | Get a field's type and its possible select options. |
Imports
| Tool | Inputs | Description |
|---|---|---|
create_import | sourceUrl, fileName?, ext?, recordedAt?, parentBoardId?, assetId?, description?, title?, customFields?, tags? | Import an asset from a URL. Optionally place it in a board, add it as a new version, and set metadata. Returns an import task ID, asset ID, version ID, and canonical url. |
get_import_status | importId | Poll an import task. Status is pending, inProgress, succeeded, or failed; failures include an error object. |
Uploads
| Tool | Inputs | Description |
|---|---|---|
upload_asset | filePath, parentBoardId?, assetId?, customFields?, tags? | Upload a local file. The SDK detects its type and upload strategy. Optionally place it in a board or add it as a new asset version. Returns the asset ID, version ID, and canonical url. |
filePath must be an absolute path that the MCP server process can read. For STDIO, this is normally the same machine as the client. For HTTP, the path refers to the server machine, not the machine running the remote client.
Tool behavior
Canonical Air URLs
get_asset, get_board, create_board, create_import, and upload_asset return a canonical url when applicable. Agents should use this field instead of constructing Air URLs from asset, version, or board IDs.
Asset images and thumbnails
get_asset_image returns an MCP image content block that supported clients can render directly. list_assets and list_nested_assets can include image content blocks by setting includeThumbnails: true.
For list calls, thumbnail fetching is limited to the first 10 returned assets. Images are fetched only from trusted Air or Imgix HTTPS hosts.
Custom-field values
update_asset_custom_field fetches the custom-field definition and validates values before updating the asset:
- For
plain-textanddatefields, passvalue. Passnullto clear it. - For
single-selectandmulti-selectfields, passvaluesas an array of{ "id": "option-uuid" }objects. Passnullto clear the selection. - A select field also accepts one option ID through
value; the server validates the ID and converts it tovalues. - Do not pass both
valueandvalues. - Select option IDs are validated against the field's available options.
The customFields parameter accepted by create_import and upload_asset uses an array of objects with this shape:
[
{
"id": "text-or-date-field-id",
"value": "text-or-date-value"
},
{
"id": "select-field-id",
"values": [{ "id": "select-option-id" }]
}
]Use the member appropriate for the field type. The tags parameter for imports and uploads is an array of { "id": "tag-id" } objects.
Imports and uploads
URL imports are asynchronous. After create_import, call get_import_status with the returned import task ID until the status is succeeded or failed.
Pass assetId to create_import or upload_asset to add the new file as a version of an existing asset. Pass parentBoardId to place a newly created asset in a board.
For create_import, fileName excludes the extension, ext excludes the leading dot, and recordedAt is an ISO 8601 date string.
Errors
Air API failures are returned as MCP tool errors containing the HTTP status and API error message. Invalid custom-field input is returned as a tool error with guidance about the expected value shape or valid select options.