Windpaint
Connect

MCP server

Connect any MCP client to the hosted Windpaint MCP server: authentication, all 20 tools, and the errors agents see.

The Windpaint MCP server gives an agent Windpaint’s generation API as MCP tools: list capabilities, estimate, upload, generate, wait, download, and run products. It’s a thin client of the public API, so every tool call is an ordinary API request made with your key, billed and scoped like one.

Hosted endpoint (streamable HTTP):

https://mcp.windpaint.ai/mcp

The quickest way to connect, from Claude Code:

claude mcp add --transport http windpaint https://mcp.windpaint.ai/mcp \
  --header "Authorization: Bearer $WINDPAINT_API_KEY"

For Claude Code, Codex and Cursor, the skills plugin installs this server along with instructions for using it. See Claude Code, Codex and Cursor.

Authentication

Every request to the hosted server must carry your API key:

HeaderRequiredValue
AuthorizationYesBearer aak_...
X-Windpaint-ProjectNoA project id or slug. Sets the default project for every tool call on this connection.

The server passes the key through to the API for each call and stores nothing. There is no OAuth sign-in; clients that can only connect through OAuth can’t use the hosted server yet. Create a key in Settings → API Keys (API keys).

Projects

Every job and asset belongs to a project. For each tool call the project is, in order:

  1. The tool’s project argument (id or slug), if given.
  2. The X-Windpaint-Project header on the connection.
  3. Your organization’s default project.

Asset ids only work as inputs inside the project they belong to.

Generic client config

Most MCP clients accept a JSON block like this. Check your client’s docs for where the file lives and whether it supports environment variable interpolation in headers, so the key doesn’t have to be written into the file.

{
  "mcpServers": {
    "windpaint": {
      "type": "http",
      "url": "https://mcp.windpaint.ai/mcp",
      "headers": {
        "Authorization": "Bearer aak_...",
        "X-Windpaint-Project": "my-project"
      }
    }
  }
}

Drop X-Windpaint-Project to use your default project.

Files

The server runs on Windpaint’s side, so it can’t see your disk:

  • Uploading: upload_asset takes a public http(s) URL, which the server fetches, or the file contents as data_base64. URLs that point at private or local addresses, or that redirect, are refused. A local file path is refused.
  • Downloading: download_asset returns a signed URL valid for 15 minutes. The agent fetches it itself, for example with curl -o out.png "<url>". The signed URL needs no key; don’t share it, and don’t store it. Call download_asset again for a fresh one.

If an agent mostly works with files on disk, it can also use the CLI, which uploads local paths and saves outputs directly.

Tools

The server also sends the client short instructions: check list_products first, estimate before large runs, wait on video instead of blocking, and chain steps by passing asset ids from one job’s outputs into the next job’s inputs.

A typical flow: list_capabilities → upload_asset (start frames, references) → estimate_cost → generate → wait_for_job → download_asset.

Catalog and cost

ToolWhat it does
list_capabilitiesEvery capability with its input slots, whether a prompt is required, allowed aspect ratios, and the models that implement it with available, resolutions, durations, licence and the current credit price per tier. Takes no arguments.
estimate_costCredits a generation would cost, without running it. Also validates the arguments and returns the chosen model, output width/height, and the organization’s available_credits and held_credits.
get_balanceThe organization’s credits: available, held, totals by_source, next expiry, and each credit lot. Takes no arguments.

estimate_cost parameters:

ParameterTypeDefaultNotes
capabilitystringrequiredFor example image.generate, video.generate
promptstring""Required by most capabilities
modelstringfirst available model
inputsobject{}{slot: [asset_id, ...]}
resolutionstringmodel default1k, 2k, 480p, … as listed
aspect_ratiostring16:9 for video, 1:1 otherwise
qualitystringnoneNo model accepts a quality tier today
duration_sintegermodel’s first durationVideo
projectstringdefault projectId or slug

credits is null when that tier has no price and can’t be submitted. available_credits and held_credits are left out when the key can’t read billing.

Generation

ToolWhat it does
upload_assetUpload a reference image, start frame, video or audio file and get back an asset. PNG, JPEG, WebP, MP4, MP3, WAV, up to 50 MiB. Free.
generateRun a capability. Returns the queued job at once, or with wait=true, polls until it finishes.
get_jobCurrent status of a job, with outputs (asset ids and URLs), images/video shortcuts, and credits estimated and charged.
wait_for_jobPoll a job until it finishes or the timeout passes. Returns the final status, or the latest status with a note to call again.
cancel_jobCancel an unfinished job. A render already running isn’t interrupted, but its output is discarded and its credits released.
download_assetGet a 15-minute signed download URL for an asset.
list_recent_jobsRecent jobs in a project, newest first, with status, outputs and credits.
list_assetsUploads and outputs in a project, newest first.

upload_asset takes exactly one of url or data_base64:

ParameterTypeDefaultNotes
urlstringPublic http(s) URL the server fetches
data_base64stringFile contents; pass filename or content_type with it
filenamestringtaken from the URL
content_typestringguessed from the filenameFor example image/png
projectstringdefault project

generate parameters:

ParameterTypeDefaultNotes
capabilitystringrequired
promptstring""
modelstringfirst available model
inputsobject{}{"start_frame": [id]} for video.generate; ids from upload_asset or an earlier job’s outputs
resolutionstringmodel default
aspect_ratiostring16:9 for video, 1:1 otherwise
qualitystringnone
duration_sintegermodel’s first duration
seedintegerrandomSet it to reproduce or vary a result
projectstringdefault project
waitbooleanfalsePoll until the job finishes
timeout_sinteger120Max 600. Only used with wait=true

Other generation tools:

ToolParameterTypeDefaultNotes
get_jobjob_idstringrequiredThe request_id from generate
wait_for_jobjob_idstringrequired
timeout_sinteger300Max 600
cancel_jobjob_idstringrequired
download_assetassetstringrequiredAsset id, or an asset URL from a job’s outputs
list_recent_jobslimitinteger201–200
projectstringdefault project
list_assetslimitinteger501–200
projectstringdefault project

Waiting tools poll every 2 seconds at first, backing off to 10 seconds, and report progress to clients that show it. Images finish in seconds; video takes minutes, so the usual pattern is generate with wait=false, then wait_for_job with timeout_s=600, called again if the job is still running.

Projects

ToolParameterTypeDefaultWhat it does
list_projectsarchivedbooleanfalseProjects in your organization. true lists archived ones.
create_projectnamestringrequiredCreate a project. Returns it with its id and slug.
slugstringderived from the nameLowercase letters, digits and hyphens
descriptionstringnone

Products

Products are multi-step workflows run with one call, such as text-to-clip.

ToolWhat it does
list_productsProducts with their input form, outputs, steps, available with unavailable_reasons, and credits_estimate at default settings.
get_productOne product by slug.
estimate_productCredits a run would cost with these inputs, validated against the product’s form, plus available_credits.
run_productStart a run. Returns the queued run, or with wait=true, polls and returns the run with named outputs.
get_runA run’s status, each step’s status and outputs, the run’s outputs, and credits.
list_runsRecent runs, newest first.
cancel_runCancel an unfinished run and its pending and running steps.
ToolParameterTypeDefaultNotes
list_productscategorystringallimage, video or audio
get_productslugstringrequired
estimate_productslugstringrequired
inputsobject{}Text and choice values as strings; media as one asset id each
projectstringdefault project
run_productslugstringrequired
inputsobject{}
projectstringdefault project
waitbooleanfalse
timeout_sinteger300Max 600
get_runrun_idstringrequired
waitbooleanfalse
timeout_sinteger300Max 600
list_runslimitinteger201–200
productstringallProduct slug
projectstringdefault project
cancel_runrun_idstringrequired

Errors agents see

Tool failures come back as MCP tool errors with a plain message. API errors are passed through as Windpaint API error <status>: <message>, using the API’s own message (see Errors).

MessageCauseWhat to do
Missing API key. Send 'Authorization: Bearer <windpaint api key>' with each request.No Authorization headerFix the client config
Windpaint API error 401: UnauthorizedKey is wrong, expired or revokedCreate a new key
Windpaint API error 403: ForbiddenThe key’s role can’t do thisUse a key with a broader role
Not enough credits. This needs 4 credits; 1.2 are available. Top up in the Windpaint dashboard (Settings → Billing), or pick a cheaper option (lower resolution, shorter duration, another model) and check estimate_cost.402 billing.insufficient_creditsBuy credits in Settings → Billing; nothing was charged
Windpaint API error 422: No model implements image.edit yet.Capability has no model todayCheck list_capabilities for available models
Windpaint API error 422: Inputs must be asset ids or URLs returned by /v1/generation.An external URL was passed in inputsupload_asset with url first, then pass the asset id
Windpaint API error 422: Input asset not found in this project.Asset id from another project, or a typoUse an asset from the same project
Windpaint API error 503: ...Model not available, or the tier has no pricePick another listed model or tier
`path` only works when the MCP server runs locally. Use url or data_base64.upload_asset was given a local file pathUpload by URL or base64
Give exactly one of path, url or data_base64.upload_asset called with none or both of url and data_base64
That URL points at a private or local address.upload_asset with an internal URLUse a public URL or data_base64
That URL redirects; pass the final URL.upload_asset with a redirecting URLResolve the redirect first
File exceeds 50 MiB.Upload too large
Cannot tell the file type; pass content_type (e.g. image/png).No extension and no content typePass content_type
'...' is not an asset id or asset URL.download_asset got something elsePass an id from outputs

A wait that runs out of time is not an error. The tool returns the job’s current status with a note such as Still in_progress after 600s; call wait_for_job again. The job is still running and still billed; resubmitting would create and bill a second job.

Next steps