Skip to main content

MCP tools

The Corte MCP server gives agents the same primitives as the REST API, shaped as tools. It speaks Streamable HTTP at https://api.corte.so/mcp and authenticates either with browser consent (OAuth 2.1) or with a normal API key.

For setup and the conceptual tour, see Connect agents with MCP. This page is the reference: every tool, every parameter, every return shape.

Transport

POST https://api.corte.so/mcp
Authorization: Bearer corte_at_… (or corte_sk_…)
Content-Type: application/json

JSON-RPC over a single POST. The server is stateless — every request is self-contained, there's no session to establish or resume. GET /mcp and DELETE /mcp return 405.

Connect from Claude Code
claude mcp add --transport http corte https://api.corte.so/mcp

A browser window opens for you to approve what the agent may reach. Any client that follows the MCP authorization spec connects this way; a client that only sends headers uses an API key instead.

Connect with OAuth

/mcp is an OAuth 2.1 protected resource. An unauthenticated call answers 401 with

WWW-Authenticate: Bearer resource_metadata="https://api.corte.so/.well-known/oauth-protected-resource"

which is enough for a client to find everything else on its own:

EndpointWhat it is
/.well-known/oauth-protected-resourceNames the resource and its authorization server (RFC 9728)
/.well-known/oauth-authorization-serverEndpoints, grants and scopes (RFC 8414)
POST /oauth/registerDynamic client registration (RFC 7591) — public clients, token_endpoint_auth_method: none
GET /oauth/authorizeSign-in and the consent screen
POST /oauth/tokenCode exchange and refresh

PKCE with S256 is required. Redirect URIs must be https, or http on loopback (localhost, 127.0.0.1, ::1) on any port. Access tokens (corte_at_…) last an hour; the refresh token rotates on every use and lasts 30 days.

A client that names no scope is granted read-write across every area except your account, which stays read-only. account:rw is never issued to a connected app — an app cannot mint API keys, change your plan, or disconnect its neighbours.

What's different from REST

Nearly every tool wraps a REST primitive, with three deliberate differences:

Tools wait by default. generate, run_application, and run_workflow block on completion up to a waitSeconds budget, then return whatever state they reached. Agents work better with one call than with a polling loop they have to invent. Pass waitSeconds: 0 to return immediately.

Results are JSON text. Every tool returns a single text content block containing pretty-printed JSON. Parse it as JSON.

Most errors are plain messages. Tool failures surface as MCP errors carrying the REST error message — Workflow not found, Unknown model, and so on. The structured { error: { code } } envelope isn't preserved. The exception is anything the account itself blocked: an empty balance, an unconfirmed address, a paid-only model, full storage. Those come back as a readable result — see When the account blocks the call.

:::warning Agents spend real credits MCP tools run as your account. Every generation or workflow run an agent starts costs exactly what clicking Generate would. Give agents their own key so you can revoke them independently, and watch Settings → Usage. :::


Account

get_credits

Current credit balance. Cheap; an agent should call it before expensive work.

Input — none.

Returns

{
"monthlyBudgetCredits": 3500,
"purchasedCredits": 500,
"spentCreditsThisPeriod": 1288,
"remainingCredits": 2712
}

list_projects

The projects the account can work in — its own and the shared ones it is a member of. This is how an agent turns "in my Playground project" into the projectId every other tool accepts; without one, work lands in and bills to defaultProjectId. Needs account:read.

No parameters.

Returns { projects, defaultProjectId }. Each project carries id, name, description, role (owner or contributor), owner ({ email, name }), remainingCredits (what the project can spend — a shared project spends its owner's credits) and updatedAt.


Credits

An agent that runs out of credits mid-task can hand you the way to fix it. Both tools return a payment link; you open it in your browser and pay there. The card never goes near the agent, and neither tool moves money on its own — until somebody pays the link, nothing has happened.

Both need account:read. Creating a link neither reads your account nor changes it, so it doesn't cost the account:rw grant that also mints API keys.

create_topup_checkout

A payment link for a one-off top-up. 100 credits per dollar; purchased credits never expire.

ParameterTypeRequiredDescription
dollarsintegeryesWhole dollars, 5–1,000. Buys dollars × 100 credits.

Returns

{
"url": "https://checkout.stripe.com/c/pay/cs_live_…",
"dollars": 25,
"credits": 2500,
"firstTopupBonusCredits": 250,
"expiresAt": "2026-08-31T14:00:00.000Z"
}

firstTopupBonusCredits is the one-time bonus this account still has coming on its first purchase, or 0 once it's been collected. expiresAt is when the link stops working, or null where the provider doesn't say.

Credits land within about a minute of payment. Poll get_credits rather than assuming, and don't retry the work that failed until the balance has actually moved.

create_subscription_checkout

A payment link for a plan. Yearly bills once at 20% off; credits still arrive monthly.

ParameterTypeRequiredDescription
tier"standard" | "pro" | "max"yesWhich plan to buy.
interval"month" | "year"noDefaults to month.

Returns { "url": string, "tier": string, "interval": string }.

Asking for the plan the account is already on is refused, the same way POST /v1/billing/checkout refuses it.

When the account blocks the call

Eight failures aren't bugs or bad arguments — the call was fine and the account is what stopped it. Any tool can return one, and it comes back as an isError result whose text is JSON:

{
"error": "insufficient_credits",
"message": "Insufficient credits",
"balance": 12,
"estimatedCredits": 40,
"nextStep": "Ask the user to add credits: call create_topup_checkout({dollars}) and give them the link — they pay in their browser and the credits land within a minute. Then poll get_credits and retry. Do not retry before the balance changes."
}
FieldWhen
errorAlways — the error code.
messageAlways — the same sentence REST would return.
balanceinsufficient_credits and plan_required: the caller's own remainingCredits. Omitted where the payer is somebody else.
estimatedCreditsWhere the refused work knew its cost — a generation's estimate, a transcription's price.
nextStepAlways — one sentence naming what to do, written for the agent.

The eight codes and what each one actually needs:

CodeWhat clears it
insufficient_creditsA top-up or a plan.
owner_insufficient_creditsThe project owner adding credits — this work is billed to them, so buying on your own account won't unblock it.
plan_requiredAny purchase. Pro models unlock on a first top-up or a plan, not on a particular tier.
email_unverifiedClicking the link in the confirmation email. Nothing to buy.
owner_email_unverifiedThe project owner confirming theirs.
account_suspended · owner_account_suspendedContacting support.
storage_limitDeleting files, or a plan with more storage. Storage is a hard quota with no overage billing.

nextStep adapts to what the calling key can actually do: a key without account:read is pointed at the app rather than at a checkout tool it can't call, and a workspace with billing turned off is never told to buy anything.


Models & generation

list_models

The generation catalog with capabilities and pricing.

ParameterTypeRequiredDescription
kind"image" | "video" | "audio" | "upscale"noFilter by model kind.

Returns { "models": [...], "total": number } — the same model objects as GET /v1/models, always the full catalog.

generate

Submit a generation on any model, and wait for it.

ParameterTypeRequiredDescription
modelstringyesModel id from list_models.
paramsobjectyesMust match the model's kind. See Generation parameters.
projectIdstringnoBill to a shared project — the owner pays.
waitSecondsnumbernoDefault 60, max 300. 0 returns immediately.

Returns { "job": GenerationJob, "estimatedCredits": number }.

If the job is still running when the wait budget expires, the job is returned in its current state — poll with get_generation. The wait timing out is not an error.

Example call
{
"model": "nano-banana-2",
"params": {
"kind": "image",
"prompt": "Editorial flat lay of a trail-running shoe on slate, cold morning light",
"aspectRatio": "4:5",
"numImages": 2
},
"waitSeconds": 120
}
Returns
{
"job": {
"id": "a91c2d7e-5f80-4c39-b6a2-13e7d0f4c885",
"model": "nano-banana-2",
"kind": "image",
"status": "succeeded",
"resultUrls": ["https://media.corte.so/…?X-Amz-Signature=…"],
"resultStorageIds": ["c74a1e08-9b6f-4d13-a205-e8f30b7c9d61"],
"costCredits": 24,
"completedAt": "2026-07-28T21:02:55.410Z"
},
"estimatedCredits": 24
}

get_generation

Fetch a generation job by id — the polling tool.

ParameterTypeRequiredDescription
jobIdstringyesJob id. Must belong to the key's account.

Returns { "job": GenerationJob }. A finished job carries results[] in place of raw result URLs: each result has its storageId, a downloadUrl on Corte's own host that opens for 24 hours without signing in, and a libraryUrl that is permanent and opens the asset in Corte for the signed-in person — the link to show them. For a permanent public URL, publish_asset.


Assets

list_assets

A project's media — images, video and audio the account uploaded, imported or generated. Reading assets needs only media:read; importing new ones is media:rw.

ParameterTypeRequiredDescription
projectIdstringnoDefaults to the default project.
kind"image" | "video" | "audio"noFilter on the content type's major part.
limit · offsetnumbernoPaging. Default limit 50.

Returns { objects, total }. Each object carries storageId, filename, contentType, sizeBytes, createdAt, origin, a downloadUrl that opens for 24 hours and a permanent libraryUrl. total counts the filtered set, so paging within a kind is safe.

Requires membership of the project — the same check the media picker makes.

get_asset

One stored file by id: its metadata plus a downloadUrl that opens for 24 hours without signing in, a thumbDownloadUrl for images and video, and a permanent libraryUrl that opens it in Corte.

ParameterTypeRequiredDescription
storageIdstringyesFrom list_assets, upload_asset, or a generation result.

Use it to check what an id actually refers to before placing it in a content document or handing it to a model.

:::note Unknown, not forbidden A file the account neither owns nor shares through a project reads as unknown. That is deliberate: a "forbidden" answer would confirm the id exists, turning the tool into a way to probe for other people's files. :::

upload_asset

Import media from an https URL into storage. Returns the storageId every other tool accepts as an input.

ParameterTypeRequiredDescription
urlstringyeshttps URL of an image, video, or audio file.
filenamestringnoOverrides the name derived from the URL.

Returns

{
"storageId": "b0e7c9a1-4d52-4f0e-8c31-77a2e5d9f6b8",
"url": "https://media.corte.so/…?X-Amz-Signature=…",
"sizeBytes": 482113
}

There is no MCP tool for uploading local bytes — agents work from URLs. For local files use the REST ticket flow.


Applications

list_applications

The curated Applications catalog with each entry's input requirements.

Input — none.

Returns { "applications": [...] } — the same key as GET /v1/applications, with the objects trimmed to what an agent needs to choose and run one:

{
"applications": [
{
"id": "flatlay-to-model",
"name": "Flatlay to Model",
"kind": "image",
"description": "Upload a flatlay of clothing or accessories and get a styled photo of a model wearing the items…",
"inputKind": "image",
"minImages": 1,
"maxImages": 1,
"acceptsText": true,
"presets": [
{ "id": "studio-white", "name": "Studio White" },
{ "id": "street-style", "name": "Street Style" }
]
}
],
"total": 43
}

Preset prompts are omitted — only id and name. Use GET /v1/applications if you need the full objects.

run_application

Run an Application and wait for the generation.

ParameterTypeRequiredDescription
applicationIdstringyesId from list_applications.
imageStorageIdsstring[]cond.For image-input Applications.
videoStorageIdstringcond.For video-input Applications.
audioStorageIdstringcond.For audio-input Applications (voice cleanup).
durationSecondsnumbercond.Input media length — required for duration-billed Applications (video upscale, voice cleanup).
textstringnoExtra guidance. Only when acceptsText is true.
presetIdstringnoA preset id from the Application.
projectIdstringnoBill to a shared project — the owner pays.
waitSecondsnumbernoDefault 60, max 300. 0 returns immediately.

Returns { "job": GenerationJob, "estimatedCredits": number }.

Validation is identical to POST /v1/applications/:id/run — the two share one implementation.


Content

Tools over the Content management API — enough for an agent to model a CMS, fill it, publish it, and hand the app a read token. Every tool calls the same code the REST routes do, so permissions, validation and versioning behave identically.

projectId is optional throughout; omitted, it means the account's default project.

list_content_types

The project's schema registry, each type with its fields and documentCount.

ParameterTypeRequiredDescription
projectIdstringnoDefaults to the default project.
limit · offsetnumbernoPaging.

get_content_type

ParameterTypeRequiredDescription
contentTypeIdstringyesType id.
versionnumbernoA historical schema version.

create_content_type

ParameterTypeRequiredDescription
projectIdstringnoDefaults to the default project.
namestringyescamelCase machine name. Immutable afterwards.
titlestringyesWhat editors see.
descriptionstringnoFree text.
fieldsobject[]yesField definitions — see field types.
titleFieldstringnoField that names documents in lists.

Reference fields need to: [typeName], and those types must already exist — create the target type first.

update_content_type

ParameterTypeRequiredDescription
contentTypeIdstringyesType id.
baseVersionnumberyesMust equal the type's latestVersion.
title · description · fields · titleFieldnoOmitted keys keep their stored value.

Changing fields mints a new schema version and goes live immediately. A stale baseVersion errors — refetch with get_content_type and retry.

delete_content_type

ParameterTypeRequiredDescription
contentTypeIdstringyesType id.
forcebooleannoAlso delete every document of this type.

Refused outright while another type embeds this one — fix those fields first. Refused while documents use it unless force, which deletes those documents too, along with every schema version. Not reversible.

list_content_documents

ParameterTypeRequiredDescription
projectIdstringnoDefaults to the default project.
typestringnoContent type machine name.
status"draft" | "published" | "changed"noFilter by state.
qstringnoSearches the type's title field (every locale of a localized one). Requires type.
missingLocalestringnoOnly documents with an empty localized field in this locale. Requires type.
limit · offsetnumbernoPaging.

Rows of a type with localized fields carry locales: per-locale coverage (complete, partial, outdated or empty, with the field paths behind it).

get_content_document

Returns the draft and published payloads, the schema, and valid/issues — whether the draft would publish. The CRDT binaries the browser editor uses are stripped; an agent has no local document to apply them to.

create_content_document

ParameterTypeRequiredDescription
projectIdstringnoDefaults to the default project.
typestringyesContent type machine name.
draftobjectnoField values keyed by field name.

Media is { "storageId": "…" }, references are { "documentId": "…" }. The document starts unpublished.

update_content_document

ParameterTypeRequiredDescription
documentIdstringyesDocument id.
draftobjectyesThe fields you are changing. Others keep their values.

A partial draft is merged over the document's current draft, so changing one field leaves the rest alone. Pass a field as null to clear it. The same rule holds one level down for a localized field: { "title": { "de": "…" } } sets the German and keeps the other locales.

No version to pass: the draft merges with anyone editing live. The response carries valid and issues; a draft saves even when invalid, and publish is what refuses.

publish_content_document

ParameterTypeRequiredDescription
documentIdstringyesDocument id.
baseVersionnumberyesThe draftVersion from get_content_document.

A mismatched baseVersion means someone edited the document since you looked. Publishing an invalid draft is refused.

unpublish_content_document

ParameterTypeRequiredDescription
documentIdstringyesDocument id.
forcebooleannoUnpublish despite inbound published references.

Removes the live copy; the draft and the revision history stay. Anything reading published content stops seeing it immediately.

delete_content_document

ParameterTypeRequiredDescription
documentIdstringyesDocument id.
forcebooleannoDelete despite inbound published references.

Deletes the draft, the published copy and the whole revision history. Not reversible — prefer unpublish_content_document when the intent is only to take something off a live site. Forcing past the reference guard leaves those references dangling.

list_content_revisions

Draft saves plus publish, unpublish and restore steps, newest first.

get_content_settings

ParameterTypeRequiredDescription
projectIdstringnoDefaults to the default project.

The project's locales (code, title, fallback, required), its default locale, and the translation settings (tone-of-voice brief, auto-translate on publish). Read it before adding a locale or translating.

update_content_settings

ParameterTypeRequiredDescription
projectIdstringnoDefaults to the default project.
defaultLocalestringnoMust be one of locales.
localesobject[]noReplaces the whole list — read it first and resend it with the change.
translationobjectno{ brief?, autoTranslateOnPublish? }; omitted keys keep their value.

Refused when a code repeats, the default is not in the list, or a fallback points at itself, at an unknown locale, or around a loop.

translate_content_document

ParameterTypeRequiredDescription
documentIdstringyesDocument id.
localesstring[]noTarget codes. Default: every locale but the source.
fieldsstring[]noLocalized field paths. Default: all of them.
sourceLocalestringnoDefault: the project's default locale.
mode"missing" | "outdated" | "all"noDefault outdated.

Machine-translates into the draft — nothing is published. missing fills empty values; outdated also redoes machine translations whose source text changed; all rewrites hand-written translations too, so confirm with the user first. Media fields are skipped. Costs credits, billed to the project owner; the response reports translated and skipped paths per locale and the credits spent. See Locales and translation.

create_content_delivery_token

ParameterTypeRequiredDescription
projectIdstringnoDefaults to the default project.
namestringyesWhat the token is for.
mode"published" | "preview"noDefault published.

Requires project ownership.

:::caution The secret is returned once create_content_delivery_token returns a secret that cannot be recovered. Write it straight into the application's configuration — a CORTE_CONTENT_TOKEN environment variable, say — before doing anything else, and don't echo it back into the conversation. Use published mode for anything a browser can read; preview reads unpublished drafts and belongs on a server. :::

Workflows

list_workflows

List workflows in a project, most recently updated first — the account's default project unless projectId is given. Scoping matches GET /v1/workflows: every member's workflows in a shared project, not just the agent's.

ParameterTypeRequiredDescription
projectIdstringnoA shared project to list; omit for the default project.

Returns

{
"workflows": [
{ "id": "7c93b1de-…", "name": "Product Reshoot Pipeline", "nodeCount": 6, "updatedAt": "2026-07-27T09:41:22.006Z", "projectId": "1b6d4f90-…" }
],
"total": 12
}

get_workflow

Inspect a workflow and discover which node ids accept run inputs. This is the tool to call before run_workflow.

ParameterTypeRequiredDescription
workflowIdstringyesWorkflow id. Same access rule as REST — project members included.

Returns

{
"workflow": { "id": "7c93b1de-…", "name": "Product Reshoot Pipeline", "nodeCount": 6 },
"nodes": [
{ "id": "productPhoto", "type": "imageInput" },
{ "id": "concept", "type": "prompt" },
{ "id": "reshoot", "type": "generateImage" },
{ "id": "final", "type": "output" }
],
"inputNodes": [
{ "nodeId": "productPhoto", "type": "imageInput", "takes": "storageId", "current": "b0e7c9a1-…" },
{ "nodeId": "concept", "type": "prompt", "takes": "text", "current": "Alpine trail-running capsule, cold dawn light" }
]
}

inputNodes is the useful part: takes says which key to send ("text" or "storageId"), and current shows the saved value that will be used if you omit the node.

run_workflow

Execute a workflow server-side with optional per-node overrides, and wait.

ParameterTypeRequiredDescription
workflowIdstringyesWorkflow id.
inputsobjectnoMap of node id → { text } or { storageId }. Omitted nodes keep saved values.
projectIdstringnoBilling context for a personal workflow run inside a shared project.
waitSecondsnumbernoDefault 120, max 600. 0 returns immediately.

Returns { "run": Run } with nodeResults populated as far as the run got.

Example call
{
"workflowId": "7c93b1de-2a48-4f60-91b7-5d0e6a2c8f13",
"inputs": {
"concept": { "text": "Alpine trail-running capsule, cold dawn light" },
"productPhoto": { "storageId": "b0e7c9a1-4d52-4f0e-8c31-77a2e5d9f6b8" }
},
"waitSeconds": 300
}

Input validation matches the REST endpoint exactly — see POST /v1/workflows/:id/execute.

get_workflow_run

Poll a run for status and per-node results.

ParameterTypeRequiredDescription
workflowIdstringyesThe workflow the run belongs to.
runIdstringyesRun id. Visibility matches REST: all members see a project workflow's runs.

Returns { "run": Run } — status plus node results with freshly signed media URLs. nodeResults grows between polls while the run is running.


Build

Apps Corte builds, hosts and deploys. Reading them needs build:read; asking an app's coding agent for a change, or deploying one, needs build:rw.

These are the tools that let an agent maintain a website on its own — read what the site is doing, ask for a change, publish it.

list_build_apps

The Build apps the account can work in — its own, and those in projects it has been invited to.

ParameterTypeRequiredDescription
limit · offsetnumbernoPaging. Default limit 25.

Returns { items, total, limit, offset }. Each item carries the id the other Build tools take, plus name, githubRepo, defaultBranch, deployTarget and accessmanage for apps the account owns or created (it may also change the address, secrets and database, or delete the app) and edit for apps shared with it as a contributor (chat, build and deploy).

get_build_app

One app's current state, in a single call.

ParameterTypeRequiredDescription
appIdstringyesFrom list_build_apps.

Returns { app, session, recentCheckpoints, checkpointTotal, recentDeploys, deployTotal }.

  • session is the app's live or parked builder thread, or null.
  • A checkpoint is a commit the app's coding agent made. Newest first; its sha is what trigger_deploy takes.
  • A deploy carries status and, once live, the public url.

The two lists are capped at 10 — a summary to act on, not a paging surface.

create_build_app

Create an app and hand its builder the brief — the "New app" step in one call. The brief is the first message on the app's thread, exactly as if the owner had typed it.

ParameterTypeRequiredDescription
namestringyesThe app's name — becomes its public subdomain (<slug>.corte.sh), so name the product, not the request.
promptstringyesThe brief the builder starts from. Answer a skill's interview inside it (see get_skill) and the builder starts without asking.
projectIdstringnoOwn the app in a shared project; defaults to the account's default project.

Returns { app, sessionId, messageId, publicUrl }. Starts a sandbox and spends the owner's credits. Returns as soon as the brief is queued — follow with get_build_thread.

update_build_app

Rename an app and/or move its public address.

ParameterTypeRequiredDescription
appIdstringyesThe app.
namestringnoNew display name.
slugstringnoNew public subdomain (<slug>.corte.sh). Lowercase letters, digits, hyphens.

Returns { app, publicUrl }. The old address stops serving once the new one is live.

get_build_thread

What the builder has said and done — the reply side of send_build_message and create_build_app.

ParameterTypeRequiredDescription
appIdstringyesThe app.
sessionIdstringnoA specific thread; defaults to the newest.
sincenumbernoOnly events after this id — pass back the previous call's lastEventId.
waitSecondsnumbernoBlock up to this long (max 300) for a reply, a question, a checkpoint or the thread ending. Default 0.

Returns { session, lastEventId, replies, pendingQuestion, checkpoints, errors, settled }. session.status is queued | provisioning | running | awaiting_input | completed | failed | cancelled. A pendingQuestion is answered with send_build_message.

list_skills · get_skill

The platform skills a builder can run — packaged procedures for a kind of site or section (scroll-world: a scroll-scrubbed camera flight through generated scenes). list_skills takes no parameters and returns { skills: [{ name, description }] }; get_skill takes name and returns { name, description, interview, body }, where interview is the list of questions a brief should answer up front. Mention the skill by name in the brief.

send_build_message

Ask an app's coding agent to change the app, in plain language, exactly as its owner would.

ParameterTypeRequiredDescription
appIdstringyesThe app to change.
textstringyesWhat you want changed.

Returns { appId, sessionId, messageId, startedSession }.

:::caution Accepted is not done This returns as soon as the request is queued, not when the change exists. The builder works asynchronously in its own sandbox. Poll get_build_app — a new checkpoint means it committed something — and remember that a committed change is not live until it is deployed. :::

If the app has no live thread, one is opened, which starts a sandbox and spends the owner's credits. startedSession tells you which happened.

trigger_deploy

Publish an app to its live URL.

ParameterTypeRequiredDescription
appIdstringyesThe app to deploy.
checkpointShastringnoDefaults to the newest checkpoint.

Returns the deploy, which starts queued. Poll get_build_app for how it ended and the URL it went to.

This makes changes live for real visitors.

:::note Unknown, not forbidden An app the account does not own reads as unknown, for the same reason assets do: a "forbidden" answer would confirm the id exists. :::


Agents

Agents are standing automations: a prompt that runs on a schedule, on a content event, or on demand, with its own scopes. Reading them needs agents:read; creating, editing or running one needs agents:rw.

These tools exist so an assistant can author an automation for someone, not just perform a task. Agents are owner-only — only a project's owner sees or manages its agents — and a project holds at most 20.

:::warning An agent is a credential Creating one hands out standing, unattended access to the account, exercised on a schedule until the agent is disabled or deleted. Two rules bound it, both enforced on this path exactly as on REST:

  • An agent can never exceed its creator. A key scoped to content:rw cannot create an agent holding build:rw. The request is refused, not trimmed.
  • The account area is refused outright, at any level and to anyone. It reaches API key creation, so a run could mint itself a credential that outlives the agent — and revocability is the whole point of a per-run credential. :::

list_agents

The agents this account has created.

ParameterTypeRequiredDescription
projectIdstringnoOne project's agents. Omit for every agent you created.
limit · offsetnumbernoPaging. Default limit 50.

Returns { agents, total, limit, offset }.

get_agent

One agent in full — its prompt, scopes, schedule, events, and lastRunAt / nextRunAt.

ParameterTypeRequiredDescription
agentIdstringyesFrom list_agents.

Returns { agent }. Read this before update_agent: an update replaces the fields it is given, so you need to know what is already there.

list_agent_recipes

The curated agent recipes — ready-made automations, each with a prompt and suggested scopes, schedule, time zone and model. Takes no parameters.

Returns { recipes }. Read it before create_agent: when one matches what the user described, create the agent with that recipe's id as recipeId so it tracks improvements to the recipe automatically.

A suggestion is not a grant — suggestedScopes are still bounded by what the caller holds, so a recipe can never widen what you can hand out.

create_agent

Create an agent.

ParameterTypeRequiredDescription
namestringyesShort label the owner will recognise.
promptstringyesThe standing instruction, run unattended. Max 20,000 characters.
scopesstring[]yesWhat it may do — "area:read" or "area:rw". See below.
projectIdstringnoDefaults to the account's default project.
descriptionstringnoOne line on what it's for.
scheduleCronstringno5-field cron. Omit for a manual-only agent.
scheduleTzstringnoIANA zone name for the cron. Defaults to UTC.
eventsstring[]noContent events that trigger a run.
modelIdstringnoDefaults to Auto.
recipeIdstringnoTrack a recipe from list_agent_recipes.
enabledbooleannoDefault true.

Returns { agent }, including the id the other agent tools take.

Scopes are the same vocabulary as API keys: content, media, integrations, generation, workflows, cuts, projects, plugins, community, build and agents, each :read or :rw. rw covers reading too. account is not grantable to an agent.

scheduleCron is standard 5-field cron — minute, hour, day-of-month, month, day-of-week. 0 9 * * 1-5 is 09:00 every weekday. scheduleTz must be an IANA zone name (America/New_York, Europe/Lisbon) — an abbreviation like EST or an offset is refused, because only a zone name follows daylight saving. Omit both and the agent is manual-only: it fires only via run_agent or an event.

events accepts exactly document.publish, document.unpublish and document.delete — a run per matching change in the agent's project. Draft saves never trigger anything.

update_agent

Edit an agent. Only the fields you send change.

ParameterTypeRequiredDescription
agentIdstringyesThe agent to edit.
everything from create_agentnoExcept projectId, which cannot change.

Returns { agent }.

Two fields replace rather than merge: scopes and events. Send the full list you want the agent to end up with, or it loses what you left out — and the replacement is bounded by what you hold, exactly as at creation.

  • scheduleCron: null removes the schedule and leaves the agent manual-only.
  • enabled: false pauses it without deleting it. Re-enabling resumes from the next slot; missed slots are not made up.

run_agent

Start a run now, without waiting for the schedule.

ParameterTypeRequiredDescription
agentIdstringyesThe agent to run.
idempotencyKeystringnoRepeat the same key to retry safely.

Returns { run } with a status of queued, launching or running.

:::caution Queued is not finished This returns as soon as the run is queued. The agent works asynchronously and spends the owner's credits, so a successful call is not the work being done. get_agent shows lastRunAt once a run lands; the full run feed — GET /v1/agents/:id/runs, and in the app — is REST-only. :::

A run is refused with a reason when the account is already at its concurrent-run limit. A disabled agent still runs when asked directly.


Integrations

The outside services a project can act through — a Shopify store, an ad account, a Notion workspace. A connection belongs to a project: every member can use it, and it never follows the person who connected it into their other projects. Reading needs integrations:read.

list_integrations

What this project is connected to, and what it could still connect.

ParameterTypeRequiredDescription
projectIdstringnoDefaults to the project this credential acts in, else the default project.

Returns { connected, available }. connected lists live connections — provider id, label, and a status of connected, expiring or expired (never credentials); the provider-specific tools like shopify_list_assets only work for these, and an expiring one needs reconnecting before its tools are reliable. available lists the providers the platform supports that the project has not connected, with connectable saying whether this server can connect them. When a request needs data no connected service provides, look here before calling it impossible — the right answer is usually to suggest connecting the provider that covers it.

Connections are made in the app, in Settings → Integrations, and only by a person — there is no tool that connects a service.

The canonical agent flow

Ask an agent for "run my Product Reshoot workflow on this photo" and a well-behaved run looks like:

  1. upload_asset { url } → a committed storageId.
  2. get_workflow { workflowId } → read inputNodes to learn the node ids and what each one takes.
  3. run_workflow { workflowId, inputs: { productPhoto: { storageId }, concept: { text } } } → waits up to 120s by default.
  4. get_workflow_run if step 3 returned still-running → outputs land in run.nodeResults[*].output.value, with storageId alongside for chaining.

For one-shot generation, the flow collapses to list_modelsgenerate, or list_applicationsrun_application.

Tool index

ToolWaits?REST equivalent
get_creditsGET /v1/account
create_topup_checkoutPOST /v1/billing/topup
create_subscription_checkoutPOST /v1/billing/checkout
list_modelsGET /v1/models
generate60s / max 300POST /v1/generations
get_generationGET /v1/generations/:id
list_assetsGET /v1/projects/:id/storage
get_assetGET /v1/storage/:storageId
upload_assetPOST /v1/uploads/import
list_applicationsGET /v1/applications
run_application60s / max 300POST /v1/applications/:id/run
list_workflowsGET /v1/workflows
get_workflowGET /v1/workflows/:id
run_workflow120s / max 600POST /v1/workflows/:id/execute
get_workflow_runGET /v1/workflows/:id/runs/:runId
list_content_typesGET /v1/projects/:id/content/types
get_content_typeGET /v1/content/types/:id
create_content_typePOST /v1/projects/:id/content/types
update_content_typePUT /v1/content/types/:id
delete_content_typeDELETE /v1/content/types/:id
list_content_documentsGET /v1/projects/:id/content/documents
get_content_documentGET /v1/content/documents/:id
create_content_documentPOST /v1/projects/:id/content/documents
update_content_documentPUT /v1/content/documents/:id
publish_content_documentPOST /v1/content/documents/:id/publish
unpublish_content_documentPOST /v1/content/documents/:id/unpublish
delete_content_documentDELETE /v1/content/documents/:id
list_content_revisionsGET /v1/content/documents/:id/revisions
create_content_delivery_tokenPOST /v1/projects/:id/content/tokens
list_build_appsnone — Build's REST is not public API
get_build_appnone — Build's REST is not public API
send_build_messagenone — Build's REST is not public API
trigger_deploynone — Build's REST is not public API
create_build_appnone — Build's REST is not public API
get_build_threadnone — Build's REST is not public API
update_build_appnone — Build's REST is not public API
list_skills · get_skillnone — platform metadata
list_integrationsGET /v1/integrations + /catalog
list_agentsGET /v1/agents
get_agentGET /v1/agents/:id
list_agent_recipesGET /v1/agent-recipes
create_agentPOST /v1/agents
update_agentPATCH /v1/agents/:id
run_agentPOST /v1/agents/:id/run

The four Build tools are the only ones with no REST counterpart on api.corte.so: Build runs in its own cluster, and MCP is how it is reached from outside the app.

Not exposed as tools

Transcriptions, project and member management, file deletion, storage listing, API key management, and the Content delivery, webhook and GraphQL endpoints have no MCP tools — they're REST-only. An agent that needs them should call the REST API directly with the same key.