Aircheck

Docs

Aircheck reads one small JSON manifest for each post your agents queue and each ad creative your pipeline renders. This page defines the format, media rules, and MCP server.

One setup with two ways to create it

Aircheck requires no installation. Aircheck has one setup, shown in the manifest format below. Two setup paths produce identical files. Aircheck cannot distinguish between them.

  1. The recommended path uses the MCP server. Add one entry to your Claude Code or Codex config. Ask your agent to set up Aircheck. The agent reads your setup, shows the proposed config and the required change for each posting job, and writes nothing until you approve. It then reports the items it queued.
  2. You can also read this page and write the JSON yourself. A cron or Airflow setup without an agent writes the same files directly. This path supports setups without an agent. The MCP server remains the recommended path.

Add the MCP server

Your account page issues this block with a machine token. Paste it into ~/.claude.json under mcpServers, or into .mcp.json in a project. Aircheck shows the token once and stores only its hash. Revoking the token locks that machine out immediately. Replace the path with the server's location on your machine.

~/.claude.json
{
  "mcpServers": {
    "aircheck": {
      "command": "node",
      "args": ["/path/to/aircheck/mcp/server.mjs"],
      "env": {
        "AIRCHECK_TOKEN": "<your token>"
      }
    }
  }
}

Codex reads the same three values from ~/.codex/config.toml.

~/.codex/config.toml
[mcp_servers.aircheck]
command = "node"
args = ["/path/to/aircheck/mcp/server.mjs"]
env = { AIRCHECK_TOKEN = "<your token>" }

Ask your agent to set up Aircheck. It calls configure, shows the proposed config and the required change for each posting job, and writes nothing until you approve.

The manifest

Write one JSON file for each queued post or ad creative in a folder named for the day. lane is the only required field. Aircheck still shows an invalid manifest as a failed item with its errors. This keeps a job typo visible instead of silently emptying the day.

<queue>/<YYYY-MM-DD>/<anything>.json
a post
{
  "lane": "brand-x",
  "at": "2026-11-07T15:30:00Z",
  "kind": "carousel",
  "text": "Post copy exactly as it will go out",
  "media": ["slide-1.png", "https://cdn.you.com/slide-2.png"],
  "status": "queued",
  "postUrl": null
}
fieldmeaning
laneWhich account or lane this goes out on. For an ad creative, the ad platform or account it runs on. Required.
atWhen it sends, ISO 8601. Omit for unscheduled.
kindtext, image, carousel, video, thread, article. Inferred from media when absent.
textThe copy, exactly as it will post.
mediaOrdered list of media sources. See Media sources.
statusA post: queued, sent, held, failed (default queued). An ad creative: draft, review, approved, live, archived (default draft).
adPresent only on an ad creative. See below.
postUrlThe permalink, after sending. Posts only.

After a send, the job or MCP server rewrites the same file. status becomes sent, and postUrl is filled in. The file's other fields stay unchanged.

An ad creative uses the same manifest

An ad creative uses the same manifest with an ad block. The ad block distinguishes a creative from a post. A file without ad is a post in Social, grouped by send hour. A file with ad is a creative in Ads, grouped by run. One folder can hold both files. Each view excludes the other file type.

an ad creative
{
  "lane": "meta",
  "text": "Maple oat latte is back.",
  "media": ["slide-1.png", "https://cdn.you.com/slide-2.png"],
  "status": "review",
  "ad": {
    "product": "harbor-coffee",
    "format": "carousel-feed",
    "run": "review-2026-11-07",
    "approved": false
  }
}
fieldmeaning
ad.productWhat the creative is about. Required.
ad.formatThe placement it is cut for: carousel-feed, still-square, video-portrait-15s, search-text. Defaults to the kind.
ad.runThe production run it belongs to. Creatives are grouped by run. Defaults to the dated folder.
ad.approvedA person signed off. Defaults to false.
ad.recipeThe recipe or script that built it, so Repair can re-run the real pipeline. Optional.

lane, text, media, kind, title and meta have the same meanings for posts and creatives. media follows the source rules below. at remains optional. A creative normally omits at.

no ad: a postad: a creative
ScopeSocialAds
Grouped bySend hourad.run
Statusqueued sent held faileddraft review approved live archived
Replace is lockedOnce sent or held, or within 6 minutes of its send timeOnce approved, live or archived
mark_sent writessent and postUrllive

Each manifest shape has its own status ladder. Aircheck checks the status word against the shape. Aircheck reports a post marked review or a creative marked queued as a problem. The problem lists both fixes: use the other ladder, or add or remove the ad block.

Repair may replace a creative only before anyone signs off. Aircheck refuses Replace when ad.approved is true or when status is approved, live or archived. A replace preserves approved and status. Replace cannot promote a creative.

Aircheck reads a renderer's existing manifest without changing it. The ad-catalog source reads <dir>/<product>/<run>/catalog.json. It lists one creative for each asset in that file. The renderer owns the file, so Aircheck preserves its shape.

Media sources

A media entry identifies a source. The browser resolves the source, and the bytes do not pass through Aircheck. Your account stores manifests containing a few KB of JSON each. Your account stores no media. Aircheck has no upload or upload field.

formexamplehow the browser gets it
relative path"slide-1.png"resolved against the JSON file's folder, then served by the local bridge
absolute path"/Users/you/out/slide-1.png"served by the local bridge
URL"https://cdn.you.com/slide-1.png"loaded directly from your storage
object{ "path": "...", "alt": "..." }same as above, with extras

A page served over HTTPS cannot read file:// paths. It can load http://localhost. The MCP server runs a media bridge on 127.0.0.1:3327. The bridge serves a file only when its real path, after symlink resolution, is inside a folder listed in mediaRoots. The bridge serves files only to Aircheck origins.

Where you keep media determines where you can review it

Media on your disk is available for review from that machine because its bridge serves the files. Media in your own bucket is available from anywhere, including a phone, because the browser loads it directly from the bucket. Your storage choice determines this access.

Aircheck reports a named local file that is absent from disk as missing. It does not drop the file from the manifest. Aircheck shows a post with missing slides so you can fix it before sending.

The MCP tools

The MCP server runs on your machine. It is the only part of Aircheck that runs there. It writes manifests. It reads a day back. It proposes wiring. It re-runs a post's own pipeline. It serves the media bridge.

toolwhat it does
queue_postWrite one manifest for a post the agent just queued, or a creative it just rendered (pass ad). Validates first and returns the problems instead of writing a bad file.
mark_sentAfter a send, rewrite status and postUrl. A creative goes live. Every other field is preserved.
list_queueRead a day back, with media resolved and every missing local file named.
configureRead your setup and propose the wiring. Writes the config file only with apply: true.
repairStart a re-run of one post's own pipeline on your machine. Returns a run id at once.
repair_statusHow a repair run is doing, and its result once it is done.

Repair re-runs your real pipeline on your machine. repair returns a run id immediately instead of blocking. A blocking render would hold the agent's conversation open. repair_status reports the run. Its result survives a server restart during the render.

configure writes nothing without apply: true. With apply: true, it writes only the config file. It does not edit a posting job or run one to test the wiring. It does not touch crontab, launchctl or an Airflow trigger.

The setup skill

aircheck-setup is a Claude Code and Codex skill for the manual route. It reads the machine for posting jobs, including cron entries, launchd plists, Airflow DAGs, scripts that call a platform's publish API, and scripts that drive a browser on a platform's own site. It records where each job writes media. It proposes aircheck.config.json. It proposes the change each job needs to write one manifest per post. It shows every write as a diff. It applies a change only after you approve it. It never starts a posting job or changes what a job posts.

Its two helpers run independently and read only. scan-setup.mjs lists what posts on the machine and where each job writes. check-queue.mjs reads the queue folder as Aircheck does and exits 1 when a file is unusable.

Back to Aircheck